Fix conflicts
commit
7bb1e2e40e
|
@ -2,6 +2,12 @@
|
|||
set -o pipefail
|
||||
exec 5>&1
|
||||
|
||||
if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
log_f "SKIP_LETS_ENCRYPT=y, skipping Let's Encrypt..."
|
||||
sleep 365d
|
||||
exec $(readlink -f "$0")
|
||||
fi
|
||||
|
||||
ACME_BASE=/var/lib/acme
|
||||
SSL_EXAMPLE=/var/lib/ssl-example
|
||||
|
||||
|
@ -102,11 +108,6 @@ while ! mysqladmin ping --host mysql -u${DBUSER} -p${DBPASS} --silent; do
|
|||
done
|
||||
|
||||
while true; do
|
||||
if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
log_f "SKIP_LETS_ENCRYPT=y, skipping Let's Encrypt..."
|
||||
sleep 365d
|
||||
exec $(readlink -f "$0")
|
||||
fi
|
||||
if [[ "${SKIP_IP_CHECK}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
SKIP_IP_CHECK=y
|
||||
fi
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from flask import Flask
|
||||
from flask_restful import Resource, Api
|
||||
from flask import jsonify
|
||||
from flask import request
|
||||
from threading import Thread
|
||||
import docker
|
||||
import signal
|
||||
|
@ -13,17 +14,23 @@ api = Api(app)
|
|||
class containers_get(Resource):
|
||||
def get(self):
|
||||
containers = {}
|
||||
for container in docker_client.containers.list(all=True):
|
||||
containers.update({container.attrs['Id']: container.attrs})
|
||||
return containers
|
||||
try:
|
||||
for container in docker_client.containers.list(all=True):
|
||||
containers.update({container.attrs['Id']: container.attrs})
|
||||
return containers
|
||||
except Exception as e:
|
||||
return jsonify(type='danger', msg=e)
|
||||
|
||||
class container_get(Resource):
|
||||
def get(self, container_id):
|
||||
if container_id and container_id.isalnum():
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
return container.attrs
|
||||
try:
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
return container.attrs
|
||||
except Exception as e:
|
||||
return jsonify(type='danger', msg=e)
|
||||
else:
|
||||
return jsonify(message='No or invalid id defined')
|
||||
return jsonify(type='danger', msg='no or invalid id defined')
|
||||
|
||||
class container_post(Resource):
|
||||
def post(self, container_id, post_action):
|
||||
|
@ -32,30 +39,51 @@ class container_post(Resource):
|
|||
try:
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
container.stop()
|
||||
except:
|
||||
return 'Error'
|
||||
else:
|
||||
return 'OK'
|
||||
return jsonify(type='success', msg='command completed successfully')
|
||||
except Exception as e:
|
||||
return jsonify(type='danger', msg=e)
|
||||
|
||||
elif post_action == 'start':
|
||||
try:
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
container.start()
|
||||
except:
|
||||
return 'Error'
|
||||
else:
|
||||
return 'OK'
|
||||
return jsonify(type='success', msg='command completed successfully')
|
||||
except Exception as e:
|
||||
return jsonify(type='danger', msg=e)
|
||||
|
||||
elif post_action == 'restart':
|
||||
try:
|
||||
for container in docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||
container.restart()
|
||||
except:
|
||||
return 'Error'
|
||||
return jsonify(type='success', msg='command completed successfully')
|
||||
except Exception as e:
|
||||
return jsonify(type='danger', msg=e)
|
||||
|
||||
elif post_action == 'exec':
|
||||
|
||||
if not request.json or not 'cmd' in request.json:
|
||||
return jsonify(type='danger', msg='cmd is missing')
|
||||
|
||||
if request.json['cmd'] == 'sieve_list' and request.json['username']:
|
||||
try:
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
return container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve list -u '" + request.json['username'].replace("'", "'\\''") + "'"], user='vmail')
|
||||
except Exception as e:
|
||||
return jsonify(type='danger', msg=e)
|
||||
elif request.json['cmd'] == 'sieve_print' and request.json['script_name'] and request.json['username']:
|
||||
try:
|
||||
for container in docker_client.containers.list(filters={"id": container_id}):
|
||||
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:
|
||||
return jsonify(type='danger', msg=e)
|
||||
else:
|
||||
return 'OK'
|
||||
return jsonify(type='danger', msg='Unknown command')
|
||||
|
||||
else:
|
||||
return jsonify(message='Invalid action')
|
||||
return jsonify(type='danger', msg='invalid action')
|
||||
|
||||
else:
|
||||
return jsonify(message='Invalid container id or missing action')
|
||||
return jsonify(type='danger', msg='invalid container id or missing action')
|
||||
|
||||
class GracefulKiller:
|
||||
kill_now = False
|
||||
|
|
|
@ -3,8 +3,8 @@ LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
|||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ENV LC_ALL C
|
||||
ENV DOVECOT_VERSION 2.2.32
|
||||
ENV PIGEONHOLE_VERSION 0.4.20
|
||||
ENV DOVECOT_VERSION 2.2.33.2
|
||||
ENV PIGEONHOLE_VERSION 0.4.21
|
||||
|
||||
RUN apt-get update && apt-get -y install \
|
||||
automake \
|
||||
|
@ -40,10 +40,11 @@ RUN apt-get update && apt-get -y install \
|
|||
libtest-pod-perl \
|
||||
libtest-simple-perl \
|
||||
libunicode-string-perl \
|
||||
libproc-processtable-perl \
|
||||
libproc-processtable-perl \
|
||||
liburi-perl \
|
||||
lzma-dev \
|
||||
make \
|
||||
procps \
|
||||
supervisor \
|
||||
syslog-ng \
|
||||
syslog-ng-core \
|
||||
|
@ -64,7 +65,8 @@ RUN curl https://www.dovecot.org/releases/2.2/dovecot-$DOVECOT_VERSION.tar.gz |
|
|||
&& make -j3 \
|
||||
&& make install \
|
||||
&& make clean \
|
||||
&& cd .. && rm -rf dovecot-2.2-pigeonhole-$PIGEONHOLE_VERSION
|
||||
&& cd .. \
|
||||
&& rm -rf dovecot-2.2-pigeonhole-$PIGEONHOLE_VERSION
|
||||
|
||||
RUN cpanm Data::Uniqid Mail::IMAPClient String::Util
|
||||
RUN echo '* * * * * root /usr/local/bin/imapsync_cron.pl' > /etc/cron.d/imapsync
|
||||
|
@ -98,8 +100,6 @@ RUN touch /etc/default/locale
|
|||
RUN apt-get purge -y build-essential automake autotools-dev \
|
||||
&& apt-get autoremove --purge -y
|
||||
|
||||
EXPOSE 24 10001
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ sed -i "/^\$DBNAME/c\\\$DBNAME='${DBNAME}';" /usr/local/bin/imapsync_cron.pl
|
|||
DBPASS=$(echo ${DBPASS} | sed 's/"/\\"/g')
|
||||
|
||||
# Create quota dict for Dovecot
|
||||
cat <<EOF > /usr/local/etc/dovecot/sql/dovecot-dict-sql.conf
|
||||
cat <<EOF > /usr/local/etc/dovecot/sql/dovecot-dict-sql-quota.conf
|
||||
connect = "host=mysql dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
|
||||
map {
|
||||
pattern = priv/quota/storage
|
||||
|
@ -31,8 +31,54 @@ map {
|
|||
}
|
||||
EOF
|
||||
|
||||
# Create dict used for sieve pre and postfilters
|
||||
cat <<EOF > /usr/local/etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
|
||||
connect = "host=mysql dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
|
||||
map {
|
||||
pattern = priv/sieve/name/\$script_name
|
||||
table = sieve_before
|
||||
username_field = username
|
||||
value_field = id
|
||||
fields {
|
||||
script_name = \$script_name
|
||||
}
|
||||
}
|
||||
map {
|
||||
pattern = priv/sieve/data/\$id
|
||||
table = sieve_before
|
||||
username_field = username
|
||||
value_field = script_data
|
||||
fields {
|
||||
id = \$id
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF > /usr/local/etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
|
||||
connect = "host=mysql dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
|
||||
map {
|
||||
pattern = priv/sieve/name/\$script_name
|
||||
table = sieve_after
|
||||
username_field = username
|
||||
value_field = id
|
||||
fields {
|
||||
script_name = \$script_name
|
||||
}
|
||||
}
|
||||
map {
|
||||
pattern = priv/sieve/data/\$id
|
||||
table = sieve_after
|
||||
username_field = username
|
||||
value_field = script_data
|
||||
fields {
|
||||
id = \$id
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
|
||||
# Create user and pass dict for Dovecot
|
||||
cat <<EOF > /usr/local/etc/dovecot/sql/dovecot-mysql.conf
|
||||
cat <<EOF > /usr/local/etc/dovecot/sql/dovecot-dict-sql-passdb.conf
|
||||
driver = mysql
|
||||
connect = "host=mysql dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
|
||||
default_pass_scheme = SSHA256
|
||||
|
|
|
@ -54,6 +54,10 @@ while ($row = $sth->fetchrow_arrayref()) {
|
|||
$delete1 = @$row[12];
|
||||
$delete2 = @$row[13];
|
||||
|
||||
$is_running = $dbh->prepare("UPDATE imapsync SET is_running = 1 WHERE id = ?");
|
||||
$is_running->bind_param( 1, ${id} );
|
||||
$is_running->execute();
|
||||
|
||||
if ($enc1 eq "TLS") { $enc1 = "--tls1"; } elsif ($enc1 eq "SSL") { $enc1 = "--ssl1"; } else { undef $enc1; }
|
||||
|
||||
my $template = $run_dir . '/imapsync.XXXXXXX';
|
||||
|
@ -83,7 +87,7 @@ while ($row = $sth->fetchrow_arrayref()) {
|
|||
"--passfile2", $passfile2->filename,
|
||||
'--no-modulesversion'], ">", \my $stdout;
|
||||
|
||||
$update = $dbh->prepare("UPDATE imapsync SET returned_text = ?, last_run = NOW() WHERE id = ?");
|
||||
$update = $dbh->prepare("UPDATE imapsync SET returned_text = ?, last_run = NOW(), is_running = 0 WHERE id = ?");
|
||||
$update->bind_param( 1, ${stdout} );
|
||||
$update->bind_param( 2, ${id} );
|
||||
$update->execute();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
|
||||
[program:syslog-ng]
|
||||
command=/usr/sbin/syslog-ng --foreground --no-caps
|
||||
|
|
|
@ -147,6 +147,9 @@ def watch():
|
|||
result = re.search(rule_regex, item['data'])
|
||||
if result:
|
||||
addr = result.group(1)
|
||||
ip = ipaddress.ip_address(addr.decode('ascii'))
|
||||
if ip.is_private or ip.is_loopback:
|
||||
continue
|
||||
print "%s matched rule id %d" % (addr, rule_id)
|
||||
log['time'] = int(round(time.time()))
|
||||
log['priority'] = "warn"
|
||||
|
|
|
@ -32,17 +32,20 @@ RUN apk add -U --no-cache libxml2-dev \
|
|||
imagemagick-dev \
|
||||
imagemagick \
|
||||
libtool \
|
||||
gettext-dev \
|
||||
openldap-dev \
|
||||
librsvg \
|
||||
&& pear install channel://pear.php.net/Net_IDNA2-0.2.0 \
|
||||
channel://pear.php.net/Auth_SASL-1.1.0 \
|
||||
Net_IMAP \
|
||||
Net_Sieve \
|
||||
NET_SMTP \
|
||||
Mail_mime \
|
||||
&& pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} \
|
||||
&& docker-php-ext-enable redis apcu memcached imagick \
|
||||
&& pecl clear-cache \
|
||||
&& docker-php-ext-configure intl \
|
||||
&& docker-php-ext-install -j 4 intl 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 \
|
||||
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \
|
||||
&& docker-php-ext-install -j 4 imap \
|
||||
&& apk del --purge autoconf g++ make libxml2-dev icu-dev imap-dev openssl-dev cyrus-sasl-dev pcre-dev libpng-dev libpng-dev libjpeg-turbo-dev libwebp-dev zlib-dev imagemagick-dev \
|
||||
|
@ -56,7 +59,6 @@ RUN apk add -U --no-cache libxml2-dev \
|
|||
echo 'opcache.revalidate_freq=1'; \
|
||||
} > /usr/local/etc/php/conf.d/opcache-recommended.ini
|
||||
|
||||
|
||||
COPY ./docker-entrypoint.sh /
|
||||
|
||||
EXPOSE 9000
|
||||
|
|
|
@ -28,13 +28,19 @@ progress() {
|
|||
[[ ${CURRENT} -gt ${TOTAL} ]] && return
|
||||
[[ ${CURRENT} -lt 0 ]] && CURRENT=0
|
||||
PERCENT=$(( 200 * ${CURRENT} / ${TOTAL} % 2 + 100 * ${CURRENT} / ${TOTAL} ))
|
||||
echo -ne "$(date) - ${SERVICE} health level: \e[7m${PERCENT}%\e[0m (${CURRENT}/${TOTAL}), health trend: "
|
||||
[[ ${DIFF} =~ ^-[1-9] ]] && echo -en '[\e[41m \e[0m] ' || echo -en '[\e[42m \e[0m] '
|
||||
echo "(${DIFF})"
|
||||
log_msg "${SERVICE} health level: ${PERCENT}% (${CURRENT}/${TOTAL}), health trend: ${DIFF}"
|
||||
log_data "$(printf "%d,%d,%d,%d" ${PERCENT} ${CURRENT} ${TOTAL} ${DIFF})" "${SERVICE}"
|
||||
}
|
||||
|
||||
log_to_redis() {
|
||||
redis-cli -h redis LPUSH WATCHDOG_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${1}")\"}"
|
||||
log_msg() {
|
||||
redis-cli -h redis LPUSH WATCHDOG_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${1}")\"}" > /dev/null
|
||||
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() {
|
||||
|
@ -43,8 +49,7 @@ function mail_error() {
|
|||
RCPT_DOMAIN=$(echo ${1} | awk -F @ {'print $NF'})
|
||||
RCPT_MX=$(dig +short ${RCPT_DOMAIN} mx | sort -n | awk '{print $2; exit}')
|
||||
if [[ -z ${RCPT_MX} ]]; then
|
||||
log_to_redis "Cannot determine MX for ${1}, skipping email notification..."
|
||||
echo "Cannot determine MX for ${1}"
|
||||
log_msg "Cannot determine MX for ${1}, skipping email notification..."
|
||||
return 1
|
||||
fi
|
||||
./smtp-cli --missing-modules-ok \
|
||||
|
@ -54,6 +59,7 @@ function mail_error() {
|
|||
--from="watchdog@${MAILCOW_HOSTNAME}" \
|
||||
--server="${RCPT_MX}" \
|
||||
--hello-host=${MAILCOW_HOSTNAME}
|
||||
log_msg "Sent notification email to ${1}"
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,8 +72,8 @@ get_container_ip() {
|
|||
sleep 1
|
||||
CONTAINER_ID=$(curl --silent http://dockerapi:8080/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"${1}\")) | .id")
|
||||
if [[ ! -z ${CONTAINER_ID} ]]; then
|
||||
CONTAINER_IP=$(curl --silent http://dockerapi:8080/containers/${CONTAINER_ID}/json | jq -r '.NetworkSettings.Networks[].IPAddress')
|
||||
fi
|
||||
CONTAINER_IP=$(curl --silent http://dockerapi:8080/containers/${CONTAINER_ID}/json | jq -r '.NetworkSettings.Networks[].IPAddress')
|
||||
fi
|
||||
LOOP_C=$((LOOP_C + 1))
|
||||
done
|
||||
[[ ${LOOP_C} -gt 5 ]] && echo 240.0.0.0 || echo ${CONTAINER_IP}
|
||||
|
@ -253,9 +259,8 @@ dns_checks() {
|
|||
(
|
||||
while true; do
|
||||
if ! nginx_checks; then
|
||||
log_to_redis "Nginx hit error limit"
|
||||
log_msg "Nginx hit error limit"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${WATCHDOG_NOTIFY_EMAIL}" "nginx-mailcow"
|
||||
echo -e "\e[31m$(date) - Nginx hit error limit\e[0m"
|
||||
echo nginx-mailcow > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
|
@ -265,9 +270,8 @@ BACKGROUND_TASKS+=($!)
|
|||
(
|
||||
while true; do
|
||||
if ! mysql_checks; then
|
||||
log_to_redis "MySQL hit error limit"
|
||||
log_msg "MySQL hit error limit"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${WATCHDOG_NOTIFY_EMAIL}" "mysql-mailcow"
|
||||
echo -e "\e[31m$(date) - MySQL hit error limit\e[0m"
|
||||
echo mysql-mailcow > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
|
@ -277,9 +281,8 @@ BACKGROUND_TASKS+=($!)
|
|||
(
|
||||
while true; do
|
||||
if ! phpfpm_checks; then
|
||||
log_to_redis "PHP-FPM hit error limit"
|
||||
log_msg "PHP-FPM hit error limit"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${WATCHDOG_NOTIFY_EMAIL}" "php-fpm-mailcow"
|
||||
echo -e "\e[31m$(date) - PHP-FPM hit error limit\e[0m"
|
||||
echo php-fpm-mailcow > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
|
@ -289,9 +292,8 @@ BACKGROUND_TASKS+=($!)
|
|||
(
|
||||
while true; do
|
||||
if ! sogo_checks; then
|
||||
log_to_redis "SOGo hit error limit"
|
||||
log_msg "SOGo hit error limit"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${WATCHDOG_NOTIFY_EMAIL}" "sogo-mailcow"
|
||||
echo -e "\e[31m$(date) - SOGo hit error limit\e[0m"
|
||||
echo sogo-mailcow > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
|
@ -301,9 +303,8 @@ BACKGROUND_TASKS+=($!)
|
|||
(
|
||||
while true; do
|
||||
if ! postfix_checks; then
|
||||
log_to_redis "Postfix hit error limit"
|
||||
log_msg "Postfix hit error limit"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${WATCHDOG_NOTIFY_EMAIL}" "postfix-mailcow"
|
||||
echo -e "\e[31m$(date) - Postfix hit error limit\e[0m"
|
||||
echo postfix-mailcow > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
|
@ -313,9 +314,8 @@ BACKGROUND_TASKS+=($!)
|
|||
(
|
||||
while true; do
|
||||
if ! dovecot_checks; then
|
||||
log_to_redis "Dovecot hit error limit"
|
||||
log_msg "Dovecot hit error limit"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${WATCHDOG_NOTIFY_EMAIL}" "dovecot-mailcow"
|
||||
echo -e "\e[31m$(date) - Dovecot hit error limit\e[0m"
|
||||
echo dovecot-mailcow > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
|
@ -325,9 +325,8 @@ BACKGROUND_TASKS+=($!)
|
|||
(
|
||||
while true; do
|
||||
if ! dns_checks; then
|
||||
log_to_redis "Unbound hit error limit"
|
||||
log_msg "Unbound hit error limit"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${WATCHDOG_NOTIFY_EMAIL}" "unbound-mailcow"
|
||||
echo -e "\e[31m$(date) - Unbound hit error limit\e[0m"
|
||||
#echo unbound-mailcow > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
|
@ -337,9 +336,8 @@ BACKGROUND_TASKS+=($!)
|
|||
(
|
||||
while true; do
|
||||
if ! rspamd_checks; then
|
||||
log_to_redis "Rspamd hit error limit"
|
||||
log_msg "Rspamd hit error limit"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${WATCHDOG_NOTIFY_EMAIL}" "rspamd-mailcow"
|
||||
echo -e "\e[31m$(date) - Rspamd hit error limit\e[0m"
|
||||
echo rspamd-mailcow > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
|
@ -351,8 +349,7 @@ BACKGROUND_TASKS+=($!)
|
|||
while true; do
|
||||
for bg_task in ${BACKGROUND_TASKS[*]}; do
|
||||
if ! kill -0 ${bg_task} 1>&2; then
|
||||
echo "Worker ${bg_task} died, stopping watchdog and waiting for respawn..."
|
||||
log_to_redis "Worker ${bg_task} died, stopping watchdog and waiting for respawn..."
|
||||
log_msg "Worker ${bg_task} died, stopping watchdog and waiting for respawn..."
|
||||
kill -TERM 1
|
||||
fi
|
||||
sleep 10
|
||||
|
@ -366,7 +363,7 @@ while true; do
|
|||
while nc -z dockerapi 8080; do
|
||||
sleep 3
|
||||
done
|
||||
echo "Cannot find dockerapi-mailcow, waiting to recover..."
|
||||
log_msg "Cannot find dockerapi-mailcow, waiting to recover..."
|
||||
kill -STOP ${BACKGROUND_TASKS[*]}
|
||||
until nc -z dockerapi 8080; do
|
||||
sleep 3
|
||||
|
@ -385,11 +382,10 @@ while true; do
|
|||
sleep 3
|
||||
CONTAINER_ID=$(curl --silent http://dockerapi:8080/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"${com_pipe_answer}\")) | .id")
|
||||
if [[ ! -z ${CONTAINER_ID} ]]; then
|
||||
log_to_redis "Sending restart command to ${CONTAINER_ID}..."
|
||||
echo "Sending restart command to ${CONTAINER_ID}..."
|
||||
log_msg "Sending restart command to ${CONTAINER_ID}..."
|
||||
curl --silent -XPOST http://dockerapi:8080/containers/${CONTAINER_ID}/restart
|
||||
fi
|
||||
echo "Wait for restarted container to settle and continue watching..."
|
||||
log_msg "Wait for restarted container to settle and continue watching..."
|
||||
sleep 30s
|
||||
kill -CONT ${BACKGROUND_TASKS[*]}
|
||||
kill -USR1 ${BACKGROUND_TASKS[*]}
|
||||
|
|
|
@ -32,7 +32,7 @@ passdb {
|
|||
pass = yes
|
||||
}
|
||||
passdb {
|
||||
args = /usr/local/etc/dovecot/sql/dovecot-mysql.conf
|
||||
args = /usr/local/etc/dovecot/sql/dovecot-dict-sql-passdb.conf
|
||||
driver = sql
|
||||
}
|
||||
# Set doveadm_password=your-secret-password in data/conf/dovecot/extra.conf (create if missing)
|
||||
|
@ -173,6 +173,9 @@ service dict {
|
|||
group = vmail
|
||||
}
|
||||
}
|
||||
service log {
|
||||
user = dovenull
|
||||
}
|
||||
service auth {
|
||||
inet_listener auth-inet {
|
||||
port = 10001
|
||||
|
@ -185,7 +188,6 @@ service auth {
|
|||
mode = 0600
|
||||
user = vmail
|
||||
}
|
||||
user = root
|
||||
}
|
||||
service managesieve-login {
|
||||
inet_listener sieve {
|
||||
|
@ -193,10 +195,19 @@ service managesieve-login {
|
|||
}
|
||||
service_count = 1
|
||||
process_min_avail = 2
|
||||
vsz_limit = 128M
|
||||
vsz_limit = 64M
|
||||
}
|
||||
service imap-login {
|
||||
service_count = 1
|
||||
vsz_limit = 64M
|
||||
user = dovenull
|
||||
}
|
||||
service pop3-login {
|
||||
service_count = 1
|
||||
}
|
||||
service imap {
|
||||
executable = imap imap-postlogin
|
||||
user = dovenull
|
||||
}
|
||||
service managesieve {
|
||||
process_limit = 256
|
||||
|
@ -211,7 +222,7 @@ listen = *,[::]
|
|||
ssl_cert = </etc/ssl/mail/cert.pem
|
||||
ssl_key = </etc/ssl/mail/key.pem
|
||||
userdb {
|
||||
args = /usr/local/etc/dovecot/sql/dovecot-mysql.conf
|
||||
args = /usr/local/etc/dovecot/sql/dovecot-dict-sql-passdb.conf
|
||||
driver = sql
|
||||
}
|
||||
protocol imap {
|
||||
|
@ -245,17 +256,21 @@ plugin {
|
|||
# END
|
||||
sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve
|
||||
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
|
||||
sieve_after = /var/vmail/sieve/global.sieve
|
||||
sieve_max_script_size = 1M
|
||||
sieve_quota_max_scripts = 0
|
||||
sieve_quota_max_storage = 0
|
||||
listescape_char = "\\"
|
||||
sieve_before = dict:proxy::sieve_before;name=active;bindir=/var/vmail/sieve_before_bindir
|
||||
sieve_after = dict:proxy::sieve_after;name=active;bindir=/var/vmail/sieve_after_bindir
|
||||
sieve_after2 = /var/vmail/sieve/global.sieve
|
||||
#mail_crypt_global_private_key = </mail_crypt/ecprivkey.pem
|
||||
#mail_crypt_global_public_key = </mail_crypt/ecpubkey.pem
|
||||
#mail_crypt_save_version = 2
|
||||
}
|
||||
dict {
|
||||
sqlquota = mysql:/usr/local/etc/dovecot/sql/dovecot-dict-sql.conf
|
||||
sqlquota = mysql:/usr/local/etc/dovecot/sql/dovecot-dict-sql-quota.conf
|
||||
sieve_after = mysql:/usr/local/etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
|
||||
sieve_before = mysql:/usr/local/etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
|
||||
}
|
||||
remote 127.0.0.1 {
|
||||
disable_plaintext_auth = no
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
dns {
|
||||
enable_dnssec = true;
|
||||
}
|
||||
map_watch_interval = 60s;
|
||||
map_watch_interval = 30s;
|
||||
dns {
|
||||
timeout = 4s;
|
||||
retransmits = 5;
|
||||
|
|
|
@ -43,7 +43,9 @@
|
|||
SOGoInternalSyncInterval = 30;
|
||||
SOGoMaximumSyncInterval = 354;
|
||||
|
||||
// 100 seems to break some Android clients
|
||||
//SOGoMaximumSyncWindowSize = 100;
|
||||
// This should do the trick for Outlook 2016
|
||||
SOGoMaximumSyncResponseSize = 2048;
|
||||
|
||||
WOWatchDogRequestTimeout = 10;
|
||||
|
|
|
@ -457,12 +457,11 @@ $tfa_data = get_tfa();
|
|||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-postfix-logs">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Postfix
|
||||
<div class="panel-heading">Postfix <span class="badge badge-info log-lines"></span>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" id="refresh_postfix_log"><?=$lang['admin']['refresh'];?></a></li>
|
||||
</ul>
|
||||
<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">
|
||||
|
@ -475,12 +474,11 @@ $tfa_data = get_tfa();
|
|||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Dovecot
|
||||
<div class="panel-heading">Dovecot <span class="badge badge-info log-lines"></span>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" id="refresh_dovecot_log"><?=$lang['admin']['refresh'];?></a></li>
|
||||
</ul>
|
||||
<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">
|
||||
|
@ -493,12 +491,11 @@ $tfa_data = get_tfa();
|
|||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-sogo-logs">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">SOGo
|
||||
<div class="panel-heading">SOGo <span class="badge badge-info log-lines"></span>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" id="refresh_sogo_log"><?=$lang['admin']['refresh'];?></a></li>
|
||||
</ul>
|
||||
<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">
|
||||
|
@ -511,12 +508,11 @@ $tfa_data = get_tfa();
|
|||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-fail2ban-logs">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Fail2ban
|
||||
<div class="panel-heading">Fail2ban <span class="badge badge-info log-lines"></span>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" id="refresh_fail2ban_log"><?=$lang['admin']['refresh'];?></a></li>
|
||||
</ul>
|
||||
<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">
|
||||
|
@ -529,17 +525,16 @@ $tfa_data = get_tfa();
|
|||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-rspamd-history">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Rspamd history
|
||||
<div class="panel-heading">Rspamd history <span class="badge badge-info log-lines"></span>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" id="refresh_rspamd_history"><?=$lang['admin']['refresh'];?></a></li>
|
||||
</ul>
|
||||
<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" id="rspamd_history"></table>
|
||||
<table class="table table-striped table-condensed log-table" id="rspamd_history"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -547,12 +542,11 @@ $tfa_data = get_tfa();
|
|||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-autodiscover-logs">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Autodiscover
|
||||
<div class="panel-heading">Autodiscover <span class="badge badge-info log-lines"></span>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" id="refresh_autodiscover_log"><?=$lang['admin']['refresh'];?></a></li>
|
||||
</ul>
|
||||
<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">
|
||||
|
|
|
@ -35,7 +35,8 @@ $opt = [
|
|||
];
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
$login_user = strtolower(trim($_SERVER['PHP_AUTH_USER']));
|
||||
$login_role = check_login($login_user, $_SERVER['PHP_AUTH_PW']);
|
||||
$login_pass = trim(htmlspecialchars_decode($_SERVER['PHP_AUTH_PW']));
|
||||
$login_role = check_login($login_user, $login_pass);
|
||||
|
||||
if (!isset($_SERVER['PHP_AUTH_USER']) OR $login_role !== "user") {
|
||||
try {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -34,4 +34,4 @@ table.footable>tbody>tr.footable-empty>td {
|
|||
}
|
||||
.inputMissingAttr {
|
||||
border-color: #FF4136;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
div.numberedtextarea-wrapper{position:relative}div.numberedtextarea-wrapper textarea{display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.numberedtextarea-line-numbers{position:absolute;top:0;left:0;right:0;bottom:0;width:50px;border-right:none;color:rgba(0,0,0,.4);overflow:hidden}div.numberedtextarea-number{padding-right:6px;text-align:right}textarea#script_data{font-family:Monospace}
|
|
@ -30,3 +30,10 @@ table.footable>tbody>tr.footable-empty>td {
|
|||
.inputMissingAttr {
|
||||
border-color: #FF4136;
|
||||
}
|
||||
#logText {
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
<?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();
|
||||
}
|
||||
?>
|
|
@ -484,19 +484,25 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
</form>
|
||||
<hr>
|
||||
<form data-id="mboxratelimit" class="form-inline well" method="post">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Ratelimit</label>
|
||||
<input name="rl_value" id="rl_value" type="number" value="<?=(!empty($rl['value'])) ? $rl['value'] : null;?>" class="form-control" placeholder="disabled">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select name="rl_frame" id="rl_frame" class="form-control">
|
||||
<option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
|
||||
<option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
|
||||
<option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-default" id="edit_selected" data-id="mboxratelimit" data-item="<?=$mailbox;?>" data-api-url='edit/ratelimit' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
|
||||
<div class="row">
|
||||
<div class="col-sm-1">
|
||||
<p class="help-block">Ratelimit</p>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<div class="form-group">
|
||||
<input name="rl_value" id="rl_value" type="number" value="<?=(!empty($rl['value'])) ? $rl['value'] : null;?>" class="form-control" placeholder="disabled">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select name="rl_frame" id="rl_frame" class="form-control">
|
||||
<option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
|
||||
<option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
|
||||
<option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-default" id="edit_selected" data-id="mboxratelimit" data-item="<?=$mailbox;?>" data-api-url='edit/ratelimit' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
|
@ -717,6 +723,59 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin" || $_SESSION['mailcow_cc_role'] == "user") {
|
||||
if (isset($_GET['filter']) &&
|
||||
is_numeric($_GET['filter'])) {
|
||||
$id = $_GET["filter"];
|
||||
$result = mailbox('get', 'filter_details', $id);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<h4>Filter</h4>
|
||||
<form class="form-horizontal" data-id="editfilter" role="form" method="post">
|
||||
<input type="hidden" value="0" name="active">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="script_desc"><?=$lang['edit']['sieve_desc'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="script_desc" id="script_desc" value="<?=htmlspecialchars($result['script_desc'], ENT_QUOTES, 'UTF-8');?>" required maxlength="255">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="filter_type"><?=$lang['edit']['sieve_type'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select id="addFilterType" name="filter_type" id="filter_type" required>
|
||||
<option value="prefilter" <?=($result['filter_type'] == 'prefilter') ? 'selected' : null;?>>Prefilter</option>
|
||||
<option value="postfilter" <?=($result['filter_type'] == 'postfilter') ? 'selected' : null;?>>Postfilter</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="script_data">Script:</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="20" id="script_data" name="script_data" required><?=$result['script_data'];?></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" <?=($result['active_int']=="1") ? "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="editfilter" data-item="<?=htmlspecialchars($result['id']);?>" data-api-url='edit/filter' data-api-attr='{}' href="#"><?=$lang['edit']['validate_save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
?>
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
session_start();
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||
header('Content-Type: application/json');
|
||||
if (!isset($_SESSION['mailcow_cc_role'])) {
|
||||
exit();
|
||||
}
|
||||
if (isset($_GET['script'])) {
|
||||
$sieve = new Sieve\SieveParser();
|
||||
try {
|
||||
if (empty($_GET['script'])) {
|
||||
echo json_encode(array('type' => 'danger', 'msg' => 'Script cannot be empty'));
|
||||
exit();
|
||||
}
|
||||
$sieve->parse($_GET['script']);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo json_encode(array('type' => 'danger', 'msg' => $e->getMessage()));
|
||||
exit();
|
||||
}
|
||||
echo json_encode(array('type' => 'success', 'msg' => $lang['add']['validation_success']));
|
||||
}
|
||||
?>
|
|
@ -0,0 +1,39 @@
|
|||
<?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;
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,15 @@
|
|||
<?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['id']) && is_numeric($_GET['id'])) {
|
||||
if ($details = mailbox('get', 'syncjob_details', intval($_GET['id']))) {
|
||||
echo (empty($details['log'])) ? '-' : $details['log'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -8,6 +8,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/footer.php';
|
|||
<script src="/js/bootstrap-select.min.js"></script>
|
||||
<script src="/js/bootstrap-filestyle.min.js"></script>
|
||||
<script src="/js/notifications.min.js"></script>
|
||||
<script src="/js/numberedtextarea.min.js"></script>
|
||||
<script src="/js/u2f-api.js"></script>
|
||||
<script src="/js/api.js"></script>
|
||||
<script>
|
||||
|
@ -21,12 +22,17 @@ function setLang(sel) {
|
|||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
function mailcow_alert_box(message, type) {
|
||||
window.mailcow_alert_box = function(message, type) {
|
||||
msg = $('<span/>').html(message).text();
|
||||
$.notify({message: msg},{type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
|
||||
if (type == 'danger') {
|
||||
auto_hide = 0;
|
||||
} else {
|
||||
auto_hide = 5000;
|
||||
}
|
||||
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
|
||||
}
|
||||
<?php if (isset($_SESSION['return'])): ?>
|
||||
mailcow_alert_box("<?= $_SESSION['return']['msg']; ?>", "<?= $_SESSION['return']['type']; ?>");
|
||||
mailcow_alert_box(<?=json_encode($_SESSION['return']['msg']); ?>, "<?= $_SESSION['return']['type']; ?>");
|
||||
<?php endif; unset($_SESSION['return']); ?>
|
||||
// Confirm TFA modal
|
||||
<?php if (isset($_SESSION['pending_tfa_method'])):?>
|
||||
|
@ -170,7 +176,7 @@ $(document).ready(function() {
|
|||
$('#statusTriggerRestartSogo').text('Stopping SOGo workers, this may take a while... ');
|
||||
$.ajax({
|
||||
method: 'get',
|
||||
url: '/inc/call_sogo_ctrl.php',
|
||||
url: '/inc/ajax/sogo_ctrl.php',
|
||||
data: {
|
||||
'ajax': true,
|
||||
'ACTION': 'stop'
|
||||
|
@ -180,7 +186,7 @@ $(document).ready(function() {
|
|||
$('#statusTriggerRestartSogo').append('<br>Starting SOGo...');
|
||||
$.ajax({
|
||||
method: 'get',
|
||||
url: '/inc/call_sogo_ctrl.php',
|
||||
url: '/inc/ajax/sogo_ctrl.php',
|
||||
data: {
|
||||
'ajax': true,
|
||||
'ACTION': 'start'
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
<?php
|
||||
session_start();
|
||||
$AuthUsers = array("admin");
|
||||
if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) {
|
||||
echo "Not allowed." . PHP_EOL;
|
||||
exit();
|
||||
}
|
||||
|
||||
function docker($service_name, $action, $post_action = null, $post_fields = null) {
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER,array( 'Content-Type: application/json' ));
|
||||
|
@ -88,34 +81,4 @@ function docker($service_name, $action, $post_action = null, $post_fields = null
|
|||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($_GET['ACTION'] == "start") {
|
||||
$retry = 0;
|
||||
while (docker('sogo-mailcow', 'info')['State']['Running'] != 1 && $retry <= 3) {
|
||||
$response = docker('sogo-mailcow', 'post', 'start');
|
||||
$last_response = (trim($response) == "\"OK\"") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response . '</span></b>';
|
||||
if (trim($response) == "\"OK\"") {
|
||||
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');
|
||||
$last_response = (trim($response) == "\"OK\"") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response . '</span></b>';
|
||||
if (trim($response) == "\"OK\"") {
|
||||
break;
|
||||
}
|
||||
usleep(1500000);
|
||||
$retry++;
|
||||
}
|
||||
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Not running</span></b>' : $last_response;
|
||||
}
|
||||
|
||||
?>
|
||||
}
|
|
@ -878,15 +878,24 @@ function get_u2f_registrations($username) {
|
|||
$sel->execute(array($username));
|
||||
return $sel->fetchAll(PDO::FETCH_OBJ);
|
||||
}
|
||||
function get_logs($container, $lines = 100) {
|
||||
function get_logs($container, $lines = false) {
|
||||
if ($lines === false) {
|
||||
$lines = $GLOBALS['LOG_LINES'] - 1;
|
||||
}
|
||||
global $lang;
|
||||
global $redis;
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
return false;
|
||||
}
|
||||
$lines = intval($lines);
|
||||
if ($container == "dovecot-mailcow") {
|
||||
if ($data = $redis->lRange('DOVECOT_MAILLOG', 0, $lines)) {
|
||||
if (!is_numeric($lines)) {
|
||||
list ($from, $to) = explode('-', $lines);
|
||||
$data = $redis->lRange('DOVECOT_MAILLOG', intval($from), intval($to));
|
||||
}
|
||||
else {
|
||||
$data = $redis->lRange('DOVECOT_MAILLOG', 0, intval($lines));
|
||||
}
|
||||
if ($data) {
|
||||
foreach ($data as $json_line) {
|
||||
$data_array[] = json_decode($json_line, true);
|
||||
}
|
||||
|
@ -894,7 +903,14 @@ function get_logs($container, $lines = 100) {
|
|||
}
|
||||
}
|
||||
if ($container == "postfix-mailcow") {
|
||||
if ($data = $redis->lRange('POSTFIX_MAILLOG', 0, $lines)) {
|
||||
if (!is_numeric($lines)) {
|
||||
list ($from, $to) = explode('-', $lines);
|
||||
$data = $redis->lRange('POSTFIX_MAILLOG', intval($from), intval($to));
|
||||
}
|
||||
else {
|
||||
$data = $redis->lRange('POSTFIX_MAILLOG', 0, intval($lines));
|
||||
}
|
||||
if ($data) {
|
||||
foreach ($data as $json_line) {
|
||||
$data_array[] = json_decode($json_line, true);
|
||||
}
|
||||
|
@ -902,7 +918,14 @@ function get_logs($container, $lines = 100) {
|
|||
}
|
||||
}
|
||||
if ($container == "sogo-mailcow") {
|
||||
if ($data = $redis->lRange('SOGO_LOG', 0, $lines)) {
|
||||
if (!is_numeric($lines)) {
|
||||
list ($from, $to) = explode('-', $lines);
|
||||
$data = $redis->lRange('SOGO_LOG', intval($from), intval($to));
|
||||
}
|
||||
else {
|
||||
$data = $redis->lRange('SOGO_LOG', 0, intval($lines));
|
||||
}
|
||||
if ($data) {
|
||||
foreach ($data as $json_line) {
|
||||
$data_array[] = json_decode($json_line, true);
|
||||
}
|
||||
|
@ -910,7 +933,14 @@ function get_logs($container, $lines = 100) {
|
|||
}
|
||||
}
|
||||
if ($container == "fail2ban-mailcow") {
|
||||
if ($data = $redis->lRange('F2B_LOG', 0, $lines)) {
|
||||
if (!is_numeric($lines)) {
|
||||
list ($from, $to) = explode('-', $lines);
|
||||
$data = $redis->lRange('F2B_LOG', intval($from), intval($to));
|
||||
}
|
||||
else {
|
||||
$data = $redis->lRange('F2B_LOG', 0, intval($lines));
|
||||
}
|
||||
if ($data) {
|
||||
foreach ($data as $json_line) {
|
||||
$data_array[] = json_decode($json_line, true);
|
||||
}
|
||||
|
@ -918,7 +948,14 @@ function get_logs($container, $lines = 100) {
|
|||
}
|
||||
}
|
||||
if ($container == "autodiscover-mailcow") {
|
||||
if ($data = $redis->lRange('AUTODISCOVER_LOG', 0, $lines)) {
|
||||
if (!is_numeric($lines)) {
|
||||
list ($from, $to) = explode('-', $lines);
|
||||
$data = $redis->lRange('AUTODISCOVER_LOG', intval($from), intval($to));
|
||||
}
|
||||
else {
|
||||
$data = $redis->lRange('AUTODISCOVER_LOG', 0, intval($lines));
|
||||
}
|
||||
if ($data) {
|
||||
foreach ($data as $json_line) {
|
||||
$data_array[] = json_decode($json_line, true);
|
||||
}
|
||||
|
@ -927,7 +964,13 @@ function get_logs($container, $lines = 100) {
|
|||
}
|
||||
if ($container == "rspamd-history") {
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL,"http://rspamd-mailcow:11334/history");
|
||||
if (!is_numeric($lines)) {
|
||||
list ($from, $to) = explode('-', $lines);
|
||||
curl_setopt($curl, CURLOPT_URL,"http://rspamd-mailcow:11334/history?from=" . intval($from) . "&to=" . intval($to));
|
||||
}
|
||||
else {
|
||||
curl_setopt($curl, CURLOPT_URL,"http://rspamd-mailcow:11334/history?to=" . intval($lines));
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
$history = curl_exec($curl);
|
||||
if (!curl_errno($curl)) {
|
||||
|
@ -940,4 +983,4 @@ function get_logs($container, $lines = 100) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
function mailbox($_action, $_type, $_data = null) {
|
||||
function mailbox($_action, $_type, $_data = null, $attr = null) {
|
||||
global $pdo;
|
||||
global $redis;
|
||||
global $lang;
|
||||
|
@ -72,6 +72,116 @@ function mailbox($_action, $_type, $_data = null) {
|
|||
'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($usernames))
|
||||
);
|
||||
break;
|
||||
case 'filter':
|
||||
$sieve = new Sieve\SieveParser();
|
||||
if (!isset($_SESSION['acl']['filters']) || $_SESSION['acl']['filters'] != "1" ) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => sprintf($lang['danger']['access_denied'])
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (isset($_data['username']) && filter_var($_data['username'], FILTER_VALIDATE_EMAIL)) {
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data['username'])) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => sprintf($lang['danger']['access_denied'])
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
$username = $_data['username'];
|
||||
}
|
||||
}
|
||||
elseif ($_SESSION['mailcow_cc_role'] == "user") {
|
||||
$username = $_SESSION['mailcow_cc_username'];
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'No user defined'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$active = intval($_data['active']);
|
||||
$script_data = $_data['script_data'];
|
||||
$script_desc = $_data['script_desc'];
|
||||
$filter_type = $_data['filter_type'];
|
||||
if (empty($script_data)) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'Script cannot be empty'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$sieve->parse($script_data);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'Sieve parser error: ' . $e->getMessage()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (empty($script_data) || empty($script_desc)) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'Please define values for all fields'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if ($filter_type != 'postfilter' && $filter_type != 'prefilter') {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'Wrong filter type'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!empty($active)) {
|
||||
$script_name = 'active';
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE `sieve_filters` SET `script_name` = 'inactive' WHERE `username` = :username AND `filter_type` = :filter_type");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
':filter_type' => $filter_type
|
||||
));
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$script_name = 'inactive';
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("INSERT INTO `sieve_filters` (`username`, `script_data`, `script_desc`, `script_name`, `filter_type`)
|
||||
VALUES (:username, :script_data, :script_desc, :script_name, :filter_type)");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
':script_data' => $script_data,
|
||||
':script_desc' => $script_desc,
|
||||
':script_name' => $script_name,
|
||||
':filter_type' => $filter_type
|
||||
));
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'success',
|
||||
'msg' => sprintf($lang['success']['mailbox_modified'], $username)
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
case 'syncjob':
|
||||
if (!isset($_SESSION['acl']['syncjobs']) || $_SESSION['acl']['syncjobs'] != "1" ) {
|
||||
$_SESSION['return'] = array(
|
||||
|
@ -544,13 +654,13 @@ function mailbox($_action, $_type, $_data = null) {
|
|||
}
|
||||
$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain`= :alias_domain
|
||||
UNION
|
||||
SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain`= :alias_domain_in_domain");
|
||||
SELECT `domain` FROM `domain` WHERE `domain`= :alias_domain_in_domain");
|
||||
$stmt->execute(array(':alias_domain' => $alias_domain, ':alias_domain_in_domain' => $alias_domain));
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
if ($num_results != 0) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => sprintf($lang['danger']['aliasd_exists'])
|
||||
'msg' => sprintf($lang['danger']['alias_domain_invalid'])
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
@ -1280,11 +1390,12 @@ function mailbox($_action, $_type, $_data = null) {
|
|||
return false;
|
||||
}
|
||||
foreach ($ids as $id) {
|
||||
$is_now = mailbox('get', 'syncjob_details', $id);
|
||||
$is_now = mailbox('get', 'syncjob_details', $id, array('with_password'));
|
||||
if (!empty($is_now)) {
|
||||
$username = $is_now['user2'];
|
||||
$user1 = (!empty($_data['user1'])) ? $_data['user1'] : $is_now['user1'];
|
||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
|
||||
$last_run = (isset($_data['last_run'])) ? NULL : $is_now['last_run'];
|
||||
$delete2duplicates = (isset($_data['delete2duplicates'])) ? intval($_data['delete2duplicates']) : $is_now['delete2duplicates'];
|
||||
$delete1 = (isset($_data['delete1'])) ? intval($_data['delete1']) : $is_now['delete1'];
|
||||
$delete2 = (isset($_data['delete2'])) ? intval($_data['delete2']) : $is_now['delete2'];
|
||||
|
@ -1346,7 +1457,7 @@ function mailbox($_action, $_type, $_data = null) {
|
|||
return false;
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE `imapsync` SET `delete1` = :delete1, `delete2` = :delete2, `maxage` = :maxage, `subfolder2` = :subfolder2, `exclude` = :exclude, `host1` = :host1, `user1` = :user1, `password1` = :password1, `mins_interval` = :mins_interval, `port1` = :port1, `enc1` = :enc1, `delete2duplicates` = :delete2duplicates, `active` = :active
|
||||
$stmt = $pdo->prepare("UPDATE `imapsync` SET `delete1` = :delete1, `delete2` = :delete2, `maxage` = :maxage, `subfolder2` = :subfolder2, `exclude` = :exclude, `host1` = :host1, `last_run` = :last_run, `user1` = :user1, `password1` = :password1, `mins_interval` = :mins_interval, `port1` = :port1, `enc1` = :enc1, `delete2duplicates` = :delete2duplicates, `active` = :active
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(
|
||||
':delete1' => $delete1,
|
||||
|
@ -1358,6 +1469,7 @@ function mailbox($_action, $_type, $_data = null) {
|
|||
':host1' => $host1,
|
||||
':user1' => $user1,
|
||||
':password1' => $password1,
|
||||
':last_run' => $last_run,
|
||||
':mins_interval' => $mins_interval,
|
||||
':port1' => $port1,
|
||||
':enc1' => $enc1,
|
||||
|
@ -1379,6 +1491,100 @@ function mailbox($_action, $_type, $_data = null) {
|
|||
);
|
||||
return true;
|
||||
break;
|
||||
case 'filter':
|
||||
$sieve = new Sieve\SieveParser();
|
||||
if (!is_array($_data['id'])) {
|
||||
$ids = array();
|
||||
$ids[] = $_data['id'];
|
||||
}
|
||||
else {
|
||||
$ids = $_data['id'];
|
||||
}
|
||||
if (!isset($_SESSION['acl']['filters']) || $_SESSION['acl']['filters'] != "1" ) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => sprintf($lang['danger']['access_denied'])
|
||||
);
|
||||
return false;
|
||||
}
|
||||
foreach ($ids as $id) {
|
||||
$is_now = mailbox('get', 'filter_details', $id);
|
||||
if (!empty($is_now)) {
|
||||
$username = $is_now['username'];
|
||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
|
||||
$script_desc = (!empty($_data['script_desc'])) ? $_data['script_desc'] : $is_now['script_desc'];
|
||||
$script_data = (!empty($_data['script_data'])) ? $_data['script_data'] : $is_now['script_data'];
|
||||
$filter_type = (!empty($_data['filter_type'])) ? $_data['filter_type'] : $is_now['filter_type'];
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => sprintf($lang['danger']['access_denied'])
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$sieve->parse($script_data);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'Sieve parser error: ' . $e->getMessage()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if ($filter_type != 'postfilter' && $filter_type != 'prefilter') {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'Wrong filter type'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if ($active == '1') {
|
||||
$script_name = 'active';
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE `sieve_filters` SET `script_name` = 'inactive' WHERE `username` = :username AND `filter_type` = :filter_type");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
':filter_type' => $filter_type
|
||||
));
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$script_name = 'inactive';
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE `sieve_filters` SET `script_desc` = :script_desc, `script_data` = :script_data, `script_name` = :script_name, `filter_type` = :filter_type
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(
|
||||
':script_desc' => $script_desc,
|
||||
':script_data' => $script_data,
|
||||
':script_name' => $script_name,
|
||||
':filter_type' => $filter_type,
|
||||
':id' => $id
|
||||
));
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'success',
|
||||
'msg' => sprintf($lang['success']['mailbox_modified'], $username)
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
case 'alias':
|
||||
if (!is_array($_data['address'])) {
|
||||
$addresses = array();
|
||||
|
@ -2140,19 +2346,134 @@ function mailbox($_action, $_type, $_data = null) {
|
|||
}
|
||||
return $policydata;
|
||||
break;
|
||||
case 'filters':
|
||||
$filters = array();
|
||||
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$_data = $_SESSION['mailcow_cc_username'];
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `id` FROM `sieve_filters` WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $_data));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($row = array_shift($rows)) {
|
||||
$filters[] = $row['id'];
|
||||
}
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
return $filters;
|
||||
break;
|
||||
case 'filter_details':
|
||||
$filter_details = array();
|
||||
if (!is_numeric($_data)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT CASE `script_name` WHEN 'active' THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
|
||||
CASE `script_name` WHEN 'active' THEN 1 ELSE 0 END AS `active_int`,
|
||||
id,
|
||||
username,
|
||||
filter_type,
|
||||
script_data,
|
||||
script_desc
|
||||
FROM `sieve_filters`
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(':id' => $_data));
|
||||
$filter_details = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $filter_details['username'])) {
|
||||
return false;
|
||||
}
|
||||
return $filter_details;
|
||||
break;
|
||||
case 'active_user_sieve':
|
||||
$filter_details = array();
|
||||
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$_data = $_SESSION['mailcow_cc_username'];
|
||||
}
|
||||
$exec_fields = array(
|
||||
'cmd' => 'sieve_list',
|
||||
'username' => $_data
|
||||
);
|
||||
$filters = json_decode(docker('dovecot-mailcow', 'post', 'exec', $exec_fields), true);
|
||||
$filters = array_filter(explode(PHP_EOL, $filters));
|
||||
foreach ($filters as $filter) {
|
||||
if (preg_match('/.+ ACTIVE/i', $filter)) {
|
||||
$exec_fields = array(
|
||||
'cmd' => 'sieve_print',
|
||||
'script_name' => substr($filter, 0, -7),
|
||||
'username' => $_data
|
||||
);
|
||||
$filters = json_decode(docker('dovecot-mailcow', 'post', 'exec', $exec_fields), true);
|
||||
return preg_replace('/^.+\n/', '', $filters);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
case 'syncjob_details':
|
||||
$syncjobdetails = array();
|
||||
if (!is_numeric($_data)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT *,
|
||||
CONCAT(LEFT(`password1`, 3), '...') AS `password1_short`,
|
||||
`active` AS `active_int`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM `imapsync` WHERE id = :id");
|
||||
if (isset($attr) && in_array('no_log', $attr)) {
|
||||
$field_query = $pdo->query('SHOW FIELDS FROM `imapsync` WHERE FIELD NOT IN ("returned_text", "password1")');
|
||||
$fields = $field_query->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($field = array_shift($fields)) {
|
||||
$shown_fields[] = $field['Field'];
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT " . implode(',', $shown_fields) . ",
|
||||
`active` AS `active_int`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM `imapsync` WHERE id = :id");
|
||||
}
|
||||
elseif (isset($attr) && in_array('with_password', $attr)) {
|
||||
$stmt = $pdo->prepare("SELECT *,
|
||||
`active` AS `active_int`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM `imapsync` WHERE id = :id");
|
||||
}
|
||||
else {
|
||||
$field_query = $pdo->query('SHOW FIELDS FROM `imapsync` WHERE FIELD NOT IN ("password1")');
|
||||
$fields = $field_query->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($field = array_shift($fields)) {
|
||||
$shown_fields[] = $field['Field'];
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT " . implode(',', $shown_fields) . ",
|
||||
`active` AS `active_int`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM `imapsync` WHERE id = :id");
|
||||
}
|
||||
$stmt->execute(array(':id' => $_data));
|
||||
$syncjobdetails = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!empty($syncjobdetails['returned_text'])) {
|
||||
$syncjobdetails['log'] = $syncjobdetails['returned_text'];
|
||||
}
|
||||
else {
|
||||
$syncjobdetails['log'] = '';
|
||||
}
|
||||
unset($syncjobdetails['returned_text']);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
|
@ -2816,6 +3137,57 @@ function mailbox($_action, $_type, $_data = null) {
|
|||
);
|
||||
return true;
|
||||
break;
|
||||
case 'filter':
|
||||
if (!is_array($_data['id'])) {
|
||||
$ids = array();
|
||||
$ids[] = $_data['id'];
|
||||
}
|
||||
else {
|
||||
$ids = $_data['id'];
|
||||
}
|
||||
if (!isset($_SESSION['acl']['filters']) || $_SESSION['acl']['filters'] != "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' => $id
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `sieve_filters` WHERE id = :id");
|
||||
$stmt->execute(array(':id' => $id));
|
||||
$usr = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $usr)) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => sprintf($lang['danger']['access_denied'])
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare("DELETE FROM `sieve_filters` 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 filter id/s ' . implode(', ', $ids)
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
case 'time_limited_alias':
|
||||
if (!is_array($_data['address'])) {
|
||||
$addresses = array();
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<link rel="stylesheet" href="/inc/languages.min.css">
|
||||
<link rel="stylesheet" href="/css/mailcow.css">
|
||||
<link rel="stylesheet" href="/css/animate.min.css">
|
||||
<link rel="stylesheet" href="/css/numberedtextarea.min.css">
|
||||
<?= (preg_match("/mailbox.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/mailbox.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; ?>
|
||||
|
|
|
@ -3,7 +3,7 @@ function init_db_schema() {
|
|||
try {
|
||||
global $pdo;
|
||||
|
||||
$db_version = "25102017_0748";
|
||||
$db_version = "08112017_1049";
|
||||
|
||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
@ -27,7 +27,13 @@ function init_db_schema() {
|
|||
GROUP BY logged_in_as;",
|
||||
"grouped_domain_alias_address" => "CREATE VIEW grouped_domain_alias_address (username, ad_alias) AS
|
||||
SELECT username, IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ' '), '') AS ad_alias FROM mailbox
|
||||
LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username;"
|
||||
LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username;",
|
||||
"sieve_before" => "CREATE VIEW sieve_before (id, username, script_name, script_data) AS
|
||||
SELECT id, username, script_name, script_data FROM sieve_filters
|
||||
WHERE filter_type = 'prefilter';",
|
||||
"sieve_after" => "CREATE VIEW sieve_after (id, username, script_name, script_data) AS
|
||||
SELECT id, username, script_name, script_data FROM sieve_filters
|
||||
WHERE filter_type = 'postfilter';"
|
||||
);
|
||||
|
||||
$tables = array(
|
||||
|
@ -155,6 +161,36 @@ function init_db_schema() {
|
|||
),
|
||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||
),
|
||||
"sieve_filters" => array(
|
||||
"cols" => array(
|
||||
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||
"username" => "VARCHAR(255) NOT NULL",
|
||||
"script_desc" => "VARCHAR(255) NOT NULL",
|
||||
"script_name" => "ENUM('active','inactive')",
|
||||
"script_data" => "TEXT NOT NULL",
|
||||
"filter_type" => "ENUM('postfilter','prefilter')",
|
||||
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP"
|
||||
),
|
||||
"keys" => array(
|
||||
"primary" => array(
|
||||
"" => array("id")
|
||||
),
|
||||
"key" => array(
|
||||
"username" => array("username"),
|
||||
"script_desc" => array("script_desc")
|
||||
),
|
||||
"fkey" => array(
|
||||
"fk_username_sieve_global_before" => array(
|
||||
"col" => "username",
|
||||
"ref" => "mailbox.username",
|
||||
"delete" => "CASCADE",
|
||||
"update" => "NO ACTION"
|
||||
)
|
||||
)
|
||||
),
|
||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||
),
|
||||
"user_acl" => array(
|
||||
"cols" => array(
|
||||
"username" => "VARCHAR(255) NOT NULL",
|
||||
|
@ -165,6 +201,7 @@ function init_db_schema() {
|
|||
"delimiter_action" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"filters" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
),
|
||||
"keys" => array(
|
||||
"fkey" => array(
|
||||
|
@ -248,8 +285,11 @@ function init_db_schema() {
|
|||
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
||||
),
|
||||
"keys" => array(
|
||||
"primary" => array(
|
||||
"" => array("username")
|
||||
),
|
||||
"key" => array(
|
||||
"username" => array("username")
|
||||
"domain" => array("domain")
|
||||
)
|
||||
),
|
||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||
|
@ -274,6 +314,7 @@ function init_db_schema() {
|
|||
"delete2duplicates" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"delete1" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
"delete2" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
"is_running" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
"returned_text" => "TEXT",
|
||||
"last_run" => "TIMESTAMP NULL DEFAULT NULL",
|
||||
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?php namespace Sieve;
|
||||
|
||||
interface SieveDumpable
|
||||
{
|
||||
function dump();
|
||||
function text();
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php namespace Sieve;
|
||||
|
||||
require_once('SieveToken.php');
|
||||
|
||||
use Exception;
|
||||
|
||||
class SieveException extends Exception
|
||||
{
|
||||
protected $token_;
|
||||
|
||||
public function __construct(SieveToken $token, $arg)
|
||||
{
|
||||
$message = 'undefined sieve exception';
|
||||
$this->token_ = $token;
|
||||
|
||||
if (is_string($arg))
|
||||
{
|
||||
$message = $arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_array($arg))
|
||||
{
|
||||
$type = SieveToken::typeString(array_shift($arg));
|
||||
foreach($arg as $t)
|
||||
{
|
||||
$type .= ' or '. SieveToken::typeString($t);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$type = SieveToken::typeString($arg);
|
||||
}
|
||||
|
||||
$tokenType = SieveToken::typeString($token->type);
|
||||
$message = "$tokenType where $type expected near ". $token->text;
|
||||
}
|
||||
|
||||
parent::__construct('line '. $token->line .": $message");
|
||||
}
|
||||
|
||||
public function getLineNo()
|
||||
{
|
||||
return $this->token_->line;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
<?php namespace Sieve;
|
||||
|
||||
class SieveKeywordRegistry
|
||||
{
|
||||
protected $registry_ = array();
|
||||
protected $matchTypes_ = array();
|
||||
protected $comparators_ = array();
|
||||
protected $addressParts_ = array();
|
||||
protected $commands_ = array();
|
||||
protected $tests_ = array();
|
||||
protected $arguments_ = array();
|
||||
|
||||
protected static $refcount = 0;
|
||||
protected static $instance = null;
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
$keywords = simplexml_load_file(dirname(__FILE__) .'/keywords.xml');
|
||||
foreach ($keywords->children() as $keyword)
|
||||
{
|
||||
switch ($keyword->getName())
|
||||
{
|
||||
case 'matchtype':
|
||||
$type =& $this->matchTypes_;
|
||||
break;
|
||||
case 'comparator':
|
||||
$type =& $this->comparators_;
|
||||
break;
|
||||
case 'addresspart':
|
||||
$type =& $this->addressParts_;
|
||||
break;
|
||||
case 'test':
|
||||
$type =& $this->tests_;
|
||||
break;
|
||||
case 'command':
|
||||
$type =& $this->commands_;
|
||||
break;
|
||||
default:
|
||||
trigger_error('Unsupported keyword type "'. $keyword->getName()
|
||||
. '" in file "keywords/'. basename($file) .'"');
|
||||
return;
|
||||
}
|
||||
|
||||
$name = (string) $keyword['name'];
|
||||
if (array_key_exists($name, $type))
|
||||
trigger_error("redefinition of $type $name - skipping");
|
||||
else
|
||||
$type[$name] = $keyword->children();
|
||||
}
|
||||
|
||||
foreach (glob(dirname(__FILE__) .'/extensions/*.xml') as $file)
|
||||
{
|
||||
$extension = simplexml_load_file($file);
|
||||
$name = (string) $extension['name'];
|
||||
|
||||
if (array_key_exists($name, $this->registry_))
|
||||
{
|
||||
trigger_error('overwriting extension "'. $name .'"');
|
||||
}
|
||||
$this->registry_[$name] = $extension;
|
||||
}
|
||||
}
|
||||
|
||||
public static function get()
|
||||
{
|
||||
if (self::$instance == null)
|
||||
{
|
||||
self::$instance = new SieveKeywordRegistry();
|
||||
}
|
||||
|
||||
self::$refcount++;
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function put()
|
||||
{
|
||||
if (--self::$refcount == 0)
|
||||
{
|
||||
self::$instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function activate($extension)
|
||||
{
|
||||
if (!isset($this->registry_[$extension]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$xml = $this->registry_[$extension];
|
||||
|
||||
foreach ($xml->children() as $e)
|
||||
{
|
||||
switch ($e->getName())
|
||||
{
|
||||
case 'matchtype':
|
||||
$type =& $this->matchTypes_;
|
||||
break;
|
||||
case 'comparator':
|
||||
$type =& $this->comparators_;
|
||||
break;
|
||||
case 'addresspart':
|
||||
$type =& $this->addressParts_;
|
||||
break;
|
||||
case 'test':
|
||||
$type =& $this->tests_;
|
||||
break;
|
||||
case 'command':
|
||||
$type =& $this->commands_;
|
||||
break;
|
||||
case 'tagged-argument':
|
||||
$xml = $e->parameter[0];
|
||||
$this->arguments_[(string) $xml['name']] = array(
|
||||
'extends' => (string) $e['extends'],
|
||||
'rules' => $xml
|
||||
);
|
||||
continue;
|
||||
default:
|
||||
trigger_error('Unsupported extension type \''.
|
||||
$e->getName() ."' in extension '$extension'");
|
||||
return;
|
||||
}
|
||||
|
||||
$name = (string) $e['name'];
|
||||
if (!isset($type[$name]) ||
|
||||
(string) $e['overrides'] == 'true')
|
||||
{
|
||||
$type[$name] = $e->children();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function isTest($name)
|
||||
{
|
||||
return (isset($this->tests_[$name]) ? true : false);
|
||||
}
|
||||
|
||||
public function isCommand($name)
|
||||
{
|
||||
return (isset($this->commands_[$name]) ? true : false);
|
||||
}
|
||||
|
||||
public function matchtype($name)
|
||||
{
|
||||
if (isset($this->matchTypes_[$name]))
|
||||
{
|
||||
return $this->matchTypes_[$name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function addresspart($name)
|
||||
{
|
||||
if (isset($this->addressParts_[$name]))
|
||||
{
|
||||
return $this->addressParts_[$name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function comparator($name)
|
||||
{
|
||||
if (isset($this->comparators_[$name]))
|
||||
{
|
||||
return $this->comparators_[$name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function test($name)
|
||||
{
|
||||
if (isset($this->tests_[$name]))
|
||||
{
|
||||
return $this->tests_[$name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function command($name)
|
||||
{
|
||||
if (isset($this->commands_[$name]))
|
||||
{
|
||||
return $this->commands_[$name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function arguments($command)
|
||||
{
|
||||
$res = array();
|
||||
foreach ($this->arguments_ as $arg)
|
||||
{
|
||||
if (preg_match('/'.$arg['extends'].'/', $command))
|
||||
array_push($res, $arg['rules']);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function argument($name)
|
||||
{
|
||||
if (isset($this->arguments_[$name]))
|
||||
{
|
||||
return $this->arguments_[$name]['rules'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function requireStrings()
|
||||
{
|
||||
return array_keys($this->registry_);
|
||||
}
|
||||
public function matchTypes()
|
||||
{
|
||||
return array_keys($this->matchTypes_);
|
||||
}
|
||||
public function comparators()
|
||||
{
|
||||
return array_keys($this->comparators_);
|
||||
}
|
||||
public function addressParts()
|
||||
{
|
||||
return array_keys($this->addressParts_);
|
||||
}
|
||||
public function tests()
|
||||
{
|
||||
return array_keys($this->tests_);
|
||||
}
|
||||
public function commands()
|
||||
{
|
||||
return array_keys($this->commands_);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
<?php namespace Sieve;
|
||||
|
||||
include_once 'SieveTree.php';
|
||||
include_once 'SieveScanner.php';
|
||||
include_once 'SieveSemantics.php';
|
||||
include_once 'SieveException.php';
|
||||
|
||||
class SieveParser
|
||||
{
|
||||
protected $scanner_;
|
||||
protected $script_;
|
||||
protected $tree_;
|
||||
protected $status_;
|
||||
|
||||
public function __construct($script = null)
|
||||
{
|
||||
if (isset($script))
|
||||
$this->parse($script);
|
||||
}
|
||||
|
||||
public function GetParseTree()
|
||||
{
|
||||
return $this->tree_;
|
||||
}
|
||||
|
||||
public function dumpParseTree()
|
||||
{
|
||||
return $this->tree_->dump();
|
||||
}
|
||||
|
||||
public function getScriptText()
|
||||
{
|
||||
return $this->tree_->getText();
|
||||
}
|
||||
|
||||
protected function getPrevToken_($parent_id)
|
||||
{
|
||||
$childs = $this->tree_->getChilds($parent_id);
|
||||
|
||||
for ($i = count($childs); $i > 0; --$i)
|
||||
{
|
||||
$prev = $this->tree_->getNode($childs[$i-1]);
|
||||
if ($prev->is(SieveToken::Comment|SieveToken::Whitespace))
|
||||
continue;
|
||||
|
||||
// use command owning a block or list instead of previous
|
||||
if ($prev->is(SieveToken::BlockStart|SieveToken::Comma|SieveToken::LeftParenthesis))
|
||||
$prev = $this->tree_->getNode($parent_id);
|
||||
|
||||
return $prev;
|
||||
}
|
||||
|
||||
return $this->tree_->getNode($parent_id);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* methods for recursive descent start below
|
||||
*/
|
||||
public function passthroughWhitespaceComment($token)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function passthroughFunction($token)
|
||||
{
|
||||
$this->tree_->addChild($token);
|
||||
}
|
||||
|
||||
public function parse($script)
|
||||
{
|
||||
$this->script_ = $script;
|
||||
|
||||
$this->scanner_ = new SieveScanner($this->script_);
|
||||
|
||||
// Define what happens with passthrough tokens like whitespacs and comments
|
||||
$this->scanner_->setPassthroughFunc(
|
||||
array(
|
||||
$this, 'passthroughWhitespaceComment'
|
||||
)
|
||||
);
|
||||
|
||||
$this->tree_ = new SieveTree('tree');
|
||||
|
||||
$this->commands_($this->tree_->getRoot());
|
||||
|
||||
if (!$this->scanner_->nextTokenIs(SieveToken::ScriptEnd)) {
|
||||
$token = $this->scanner_->nextToken();
|
||||
throw new SieveException($token, SieveToken::ScriptEnd);
|
||||
}
|
||||
}
|
||||
|
||||
protected function commands_($parent_id)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (!$this->scanner_->nextTokenIs(SieveToken::Identifier))
|
||||
break;
|
||||
|
||||
// Get and check a command token
|
||||
$token = $this->scanner_->nextToken();
|
||||
$semantics = new SieveSemantics($token, $this->getPrevToken_($parent_id));
|
||||
|
||||
// Process eventual arguments
|
||||
$this_node = $this->tree_->addChildTo($parent_id, $token);
|
||||
$this->arguments_($this_node, $semantics);
|
||||
|
||||
$token = $this->scanner_->nextToken();
|
||||
if (!$token->is(SieveToken::Semicolon))
|
||||
{
|
||||
// TODO: check if/when semcheck is needed here
|
||||
$semantics->validateToken($token);
|
||||
|
||||
if ($token->is(SieveToken::BlockStart))
|
||||
{
|
||||
$this->tree_->addChildTo($this_node, $token);
|
||||
$this->block_($this_node, $semantics);
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new SieveException($token, SieveToken::Semicolon);
|
||||
}
|
||||
|
||||
$semantics->done($token);
|
||||
$this->tree_->addChildTo($this_node, $token);
|
||||
}
|
||||
}
|
||||
|
||||
protected function arguments_($parent_id, &$semantics)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if ($this->scanner_->nextTokenIs(SieveToken::Number|SieveToken::Tag))
|
||||
{
|
||||
// Check if semantics allow a number or tag
|
||||
$token = $this->scanner_->nextToken();
|
||||
$semantics->validateToken($token);
|
||||
$this->tree_->addChildTo($parent_id, $token);
|
||||
}
|
||||
else if ($this->scanner_->nextTokenIs(SieveToken::StringList))
|
||||
{
|
||||
$this->stringlist_($parent_id, $semantics);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->scanner_->nextTokenIs(SieveToken::TestList))
|
||||
{
|
||||
$this->testlist_($parent_id, $semantics);
|
||||
}
|
||||
}
|
||||
|
||||
protected function stringlist_($parent_id, &$semantics)
|
||||
{
|
||||
if (!$this->scanner_->nextTokenIs(SieveToken::LeftBracket))
|
||||
{
|
||||
$this->string_($parent_id, $semantics);
|
||||
return;
|
||||
}
|
||||
|
||||
$token = $this->scanner_->nextToken();
|
||||
$semantics->startStringList($token);
|
||||
$this->tree_->addChildTo($parent_id, $token);
|
||||
|
||||
if($this->scanner_->nextTokenIs(SieveToken::RightBracket)) {
|
||||
//allow empty lists
|
||||
$token = $this->scanner_->nextToken();
|
||||
$this->tree_->addChildTo($parent_id, $token);
|
||||
$semantics->endStringList();
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
$this->string_($parent_id, $semantics);
|
||||
$token = $this->scanner_->nextToken();
|
||||
|
||||
if (!$token->is(SieveToken::Comma|SieveToken::RightBracket))
|
||||
throw new SieveException($token, array(SieveToken::Comma, SieveToken::RightBracket));
|
||||
|
||||
if ($token->is(SieveToken::Comma))
|
||||
$semantics->continueStringList();
|
||||
|
||||
$this->tree_->addChildTo($parent_id, $token);
|
||||
}
|
||||
while (!$token->is(SieveToken::RightBracket));
|
||||
|
||||
$semantics->endStringList();
|
||||
}
|
||||
|
||||
protected function string_($parent_id, &$semantics)
|
||||
{
|
||||
$token = $this->scanner_->nextToken();
|
||||
$semantics->validateToken($token);
|
||||
$this->tree_->addChildTo($parent_id, $token);
|
||||
}
|
||||
|
||||
protected function testlist_($parent_id, &$semantics)
|
||||
{
|
||||
if (!$this->scanner_->nextTokenIs(SieveToken::LeftParenthesis))
|
||||
{
|
||||
$this->test_($parent_id, $semantics);
|
||||
return;
|
||||
}
|
||||
|
||||
$token = $this->scanner_->nextToken();
|
||||
$semantics->validateToken($token);
|
||||
$this->tree_->addChildTo($parent_id, $token);
|
||||
|
||||
do
|
||||
{
|
||||
$this->test_($parent_id, $semantics);
|
||||
|
||||
$token = $this->scanner_->nextToken();
|
||||
if (!$token->is(SieveToken::Comma|SieveToken::RightParenthesis))
|
||||
{
|
||||
throw new SieveException($token, array(SieveToken::Comma, SieveToken::RightParenthesis));
|
||||
}
|
||||
$this->tree_->addChildTo($parent_id, $token);
|
||||
}
|
||||
while (!$token->is(SieveToken::RightParenthesis));
|
||||
}
|
||||
|
||||
protected function test_($parent_id, &$semantics)
|
||||
{
|
||||
// Check if semantics allow an identifier
|
||||
$token = $this->scanner_->nextToken();
|
||||
$semantics->validateToken($token);
|
||||
|
||||
// Get semantics for this test command
|
||||
$this_semantics = new SieveSemantics($token, $this->getPrevToken_($parent_id));
|
||||
$this_node = $this->tree_->addChildTo($parent_id, $token);
|
||||
|
||||
// Consume eventual argument tokens
|
||||
$this->arguments_($this_node, $this_semantics);
|
||||
|
||||
// Check that all required arguments were there
|
||||
$token = $this->scanner_->peekNextToken();
|
||||
$this_semantics->done($token);
|
||||
}
|
||||
|
||||
protected function block_($parent_id, &$semantics)
|
||||
{
|
||||
$this->commands_($parent_id, $semantics);
|
||||
|
||||
$token = $this->scanner_->nextToken();
|
||||
if (!$token->is(SieveToken::BlockEnd))
|
||||
{
|
||||
throw new SieveException($token, SieveToken::BlockEnd);
|
||||
}
|
||||
$this->tree_->addChildTo($parent_id, $token);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
<?php namespace Sieve;
|
||||
|
||||
include_once('SieveToken.php');
|
||||
|
||||
class SieveScanner
|
||||
{
|
||||
public function __construct(&$script)
|
||||
{
|
||||
if ($script === null)
|
||||
return;
|
||||
|
||||
$this->tokenize($script);
|
||||
}
|
||||
|
||||
public function setPassthroughFunc($callback)
|
||||
{
|
||||
if ($callback == null || is_callable($callback))
|
||||
$this->ptFn_ = $callback;
|
||||
}
|
||||
|
||||
public function tokenize(&$script)
|
||||
{
|
||||
$pos = 0;
|
||||
$line = 1;
|
||||
|
||||
$scriptLength = mb_strlen($script);
|
||||
|
||||
$unprocessedScript = $script;
|
||||
|
||||
|
||||
//create one regex to find the right match
|
||||
//avoids looping over all possible tokens: increases performance
|
||||
$nameToType = [];
|
||||
$regex = [];
|
||||
// chr(65) == 'A'
|
||||
$i = 65;
|
||||
|
||||
foreach ($this->tokenMatch_ as $type => $subregex) {
|
||||
$nameToType[chr($i)] = $type;
|
||||
$regex[] = "(?P<". chr($i) . ">^$subregex)";
|
||||
$i++;
|
||||
}
|
||||
|
||||
$regex = '/' . join('|', $regex) . '/';
|
||||
|
||||
while ($pos < $scriptLength)
|
||||
{
|
||||
if (preg_match($regex, $unprocessedScript, $match)) {
|
||||
|
||||
// only keep the group that match and we only want matches with group names
|
||||
// we can use the group name to find the token type using nameToType
|
||||
$filterMatch = array_filter(array_filter($match), 'is_string', ARRAY_FILTER_USE_KEY);
|
||||
|
||||
// the first element in filterMatch will contain the matched group and the key will be the name
|
||||
$type = $nameToType[key($filterMatch)];
|
||||
$currentMatch = current($filterMatch);
|
||||
|
||||
//create the token
|
||||
$token = new SieveToken($type, $currentMatch, $line);
|
||||
$this->tokens_[] = $token;
|
||||
|
||||
if ($type == SieveToken::Unknown)
|
||||
return;
|
||||
|
||||
// just remove the part that we parsed: don't extract the new substring using script length
|
||||
// as mb_strlen is \theta(pos) (it's linear in the position)
|
||||
$matchLength = mb_strlen($currentMatch);
|
||||
$unprocessedScript = mb_substr($unprocessedScript, $matchLength);
|
||||
|
||||
$pos += $matchLength;
|
||||
$line += mb_substr_count($currentMatch, "\n");
|
||||
} else {
|
||||
$this->tokens_[] = new SieveToken(SieveToken::Unknown, '', $line);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->tokens_[] = new SieveToken(SieveToken::ScriptEnd, '', $line);
|
||||
}
|
||||
|
||||
public function nextTokenIs($type)
|
||||
{
|
||||
return $this->peekNextToken()->is($type);
|
||||
}
|
||||
|
||||
public function peekNextToken()
|
||||
{
|
||||
$offset = 0;
|
||||
do {
|
||||
$next = $this->tokens_[$this->tokenPos_ + $offset++];
|
||||
} while ($next->is(SieveToken::Comment|SieveToken::Whitespace));
|
||||
|
||||
return $next;
|
||||
}
|
||||
|
||||
public function nextToken()
|
||||
{
|
||||
$token = $this->tokens_[$this->tokenPos_++];
|
||||
|
||||
while ($token->is(SieveToken::Comment|SieveToken::Whitespace))
|
||||
{
|
||||
if ($this->ptFn_ != null)
|
||||
call_user_func($this->ptFn_, $token);
|
||||
|
||||
$token = $this->tokens_[$this->tokenPos_++];
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
protected $ptFn_ = null;
|
||||
protected $tokenPos_ = 0;
|
||||
protected $tokens_ = array();
|
||||
protected $tokenMatch_ = array (
|
||||
SieveToken::LeftBracket => '\[',
|
||||
SieveToken::RightBracket => '\]',
|
||||
SieveToken::BlockStart => '\{',
|
||||
SieveToken::BlockEnd => '\}',
|
||||
SieveToken::LeftParenthesis => '\(',
|
||||
SieveToken::RightParenthesis => '\)',
|
||||
SieveToken::Comma => ',',
|
||||
SieveToken::Semicolon => ';',
|
||||
SieveToken::Whitespace => '[ \r\n\t]+',
|
||||
SieveToken::Tag => ':[[:alpha:]_][[:alnum:]_]*(?=\b)',
|
||||
/*
|
||||
" # match a quotation mark
|
||||
( # start matching parts that include an escaped quotation mark
|
||||
([^"]*[^"\\\\]) # match a string without quotation marks and not ending with a backlash
|
||||
? # this also includes the empty string
|
||||
(\\\\\\\\)* # match any groups of even number of backslashes
|
||||
# (thus the character after these groups are not escaped)
|
||||
\\\\" # match an escaped quotation mark
|
||||
)* # accept any number of strings that end with an escaped quotation mark
|
||||
[^"]* # accept any trailing part that does not contain any quotation marks
|
||||
" # end of the quoted string
|
||||
*/
|
||||
SieveToken::QuotedString => '"(([^"]*[^"\\\\])?(\\\\\\\\)*\\\\")*[^"]*"',
|
||||
SieveToken::Number => '[[:digit:]]+(?:[KMG])?(?=\b)',
|
||||
SieveToken::Comment => '(?:\/\*(?:[^\*]|\*(?=[^\/]))*\*\/|#[^\r\n]*\r?(\n|$))',
|
||||
SieveToken::MultilineString => 'text:[ \t]*(?:#[^\r\n]*)?\r?\n(\.[^\r\n]+\r?\n|[^\.][^\r\n]*\r?\n)*\.\r?(\n|$)',
|
||||
SieveToken::Identifier => '[[:alpha:]_][[:alnum:]_]*(?=\b)',
|
||||
SieveToken::Unknown => '[^ \r\n\t]+'
|
||||
);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?php namespace Sieve;
|
||||
|
||||
class SieveScript
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
|
@ -0,0 +1,611 @@
|
|||
<?php namespace Sieve;
|
||||
|
||||
require_once('SieveKeywordRegistry.php');
|
||||
require_once('SieveToken.php');
|
||||
require_once('SieveException.php');
|
||||
|
||||
class SieveSemantics
|
||||
{
|
||||
protected static $requiredExtensions_ = array();
|
||||
|
||||
protected $comparator_;
|
||||
protected $matchType_;
|
||||
protected $addressPart_;
|
||||
protected $tags_ = array();
|
||||
protected $arguments_;
|
||||
protected $deps_ = array();
|
||||
protected $followupToken_;
|
||||
|
||||
public function __construct($token, $prevToken)
|
||||
{
|
||||
$this->registry_ = SieveKeywordRegistry::get();
|
||||
$command = strtolower($token->text);
|
||||
|
||||
// Check the registry for $command
|
||||
if ($this->registry_->isCommand($command))
|
||||
{
|
||||
$xml = $this->registry_->command($command);
|
||||
$this->arguments_ = $this->makeArguments_($xml);
|
||||
$this->followupToken_ = SieveToken::Semicolon;
|
||||
}
|
||||
else if ($this->registry_->isTest($command))
|
||||
{
|
||||
$xml = $this->registry_->test($command);
|
||||
$this->arguments_ = $this->makeArguments_($xml);
|
||||
$this->followupToken_ = SieveToken::BlockStart;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SieveException($token, 'unknown command '. $command);
|
||||
}
|
||||
|
||||
// Check if command may appear at this position within the script
|
||||
if ($this->registry_->isTest($command))
|
||||
{
|
||||
if (is_null($prevToken))
|
||||
throw new SieveException($token, $command .' may not appear as first command');
|
||||
|
||||
if (!preg_match('/^(if|elsif|anyof|allof|not)$/i', $prevToken->text))
|
||||
throw new SieveException($token, $command .' may not appear after '. $prevToken->text);
|
||||
}
|
||||
else if (isset($prevToken))
|
||||
{
|
||||
switch ($command)
|
||||
{
|
||||
case 'require':
|
||||
$valid_after = 'require';
|
||||
break;
|
||||
case 'elsif':
|
||||
case 'else':
|
||||
$valid_after = '(if|elsif)';
|
||||
break;
|
||||
default:
|
||||
$valid_after = $this->commandsRegex_();
|
||||
}
|
||||
|
||||
if (!preg_match('/^'. $valid_after .'$/i', $prevToken->text))
|
||||
throw new SieveException($token, $command .' may not appear after '. $prevToken->text);
|
||||
}
|
||||
|
||||
// Check for extension arguments to add to the command
|
||||
foreach ($this->registry_->arguments($command) as $arg)
|
||||
{
|
||||
switch ((string) $arg['type'])
|
||||
{
|
||||
case 'tag':
|
||||
array_unshift($this->arguments_, array(
|
||||
'type' => SieveToken::Tag,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'regex' => $this->regex_($arg),
|
||||
'call' => 'tagHook_',
|
||||
'name' => $this->name_($arg),
|
||||
'subArgs' => $this->makeArguments_($arg->children())
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->registry_->put();
|
||||
}
|
||||
|
||||
// TODO: the *Regex functions could possibly also be static properties
|
||||
protected function requireStringsRegex_()
|
||||
{
|
||||
return '('. implode('|', $this->registry_->requireStrings()) .')';
|
||||
}
|
||||
|
||||
protected function matchTypeRegex_()
|
||||
{
|
||||
return '('. implode('|', $this->registry_->matchTypes()) .')';
|
||||
}
|
||||
|
||||
protected function addressPartRegex_()
|
||||
{
|
||||
return '('. implode('|', $this->registry_->addressParts()) .')';
|
||||
}
|
||||
|
||||
protected function commandsRegex_()
|
||||
{
|
||||
return '('. implode('|', $this->registry_->commands()) .')';
|
||||
}
|
||||
|
||||
protected function testsRegex_()
|
||||
{
|
||||
return '('. implode('|', $this->registry_->tests()) .')';
|
||||
}
|
||||
|
||||
protected function comparatorRegex_()
|
||||
{
|
||||
return '('. implode('|', $this->registry_->comparators()) .')';
|
||||
}
|
||||
|
||||
protected function occurrence_($arg)
|
||||
{
|
||||
if (isset($arg['occurrence']))
|
||||
{
|
||||
switch ((string) $arg['occurrence'])
|
||||
{
|
||||
case 'optional':
|
||||
return '?';
|
||||
case 'any':
|
||||
return '*';
|
||||
case 'some':
|
||||
return '+';
|
||||
}
|
||||
}
|
||||
return '1';
|
||||
}
|
||||
|
||||
protected function name_($arg)
|
||||
{
|
||||
if (isset($arg['name']))
|
||||
{
|
||||
return (string) $arg['name'];
|
||||
}
|
||||
return (string) $arg['type'];
|
||||
}
|
||||
|
||||
protected function regex_($arg)
|
||||
{
|
||||
if (isset($arg['regex']))
|
||||
{
|
||||
return (string) $arg['regex'];
|
||||
}
|
||||
return '.*';
|
||||
}
|
||||
|
||||
protected function case_($arg)
|
||||
{
|
||||
if (isset($arg['case']))
|
||||
{
|
||||
return (string) $arg['case'];
|
||||
}
|
||||
return 'adhere';
|
||||
}
|
||||
|
||||
protected function follows_($arg)
|
||||
{
|
||||
if (isset($arg['follows']))
|
||||
{
|
||||
return (string) $arg['follows'];
|
||||
}
|
||||
return '.*';
|
||||
}
|
||||
|
||||
protected function makeValue_($arg)
|
||||
{
|
||||
if (isset($arg->value))
|
||||
{
|
||||
$res = $this->makeArguments_($arg->value);
|
||||
return array_shift($res);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an extension (test) commands parameters from XML to
|
||||
* a PHP array the {@see Semantics} class understands.
|
||||
* @param array(SimpleXMLElement) $parameters
|
||||
* @return array
|
||||
*/
|
||||
protected function makeArguments_($parameters)
|
||||
{
|
||||
$arguments = array();
|
||||
|
||||
foreach ($parameters as $arg)
|
||||
{
|
||||
// Ignore anything not a <parameter>
|
||||
if ($arg->getName() != 'parameter')
|
||||
continue;
|
||||
|
||||
switch ((string) $arg['type'])
|
||||
{
|
||||
case 'addresspart':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::Tag,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'regex' => $this->addressPartRegex_(),
|
||||
'call' => 'addressPartHook_',
|
||||
'name' => 'address part',
|
||||
'subArgs' => $this->makeArguments_($arg)
|
||||
));
|
||||
break;
|
||||
|
||||
case 'block':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::BlockStart,
|
||||
'occurrence' => '1',
|
||||
'regex' => '{',
|
||||
'name' => 'block',
|
||||
'subArgs' => $this->makeArguments_($arg)
|
||||
));
|
||||
break;
|
||||
|
||||
case 'comparator':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::Tag,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'regex' => 'comparator',
|
||||
'name' => 'comparator',
|
||||
'subArgs' => array( array(
|
||||
'type' => SieveToken::String,
|
||||
'occurrence' => '1',
|
||||
'call' => 'comparatorHook_',
|
||||
'case' => 'adhere',
|
||||
'regex' => $this->comparatorRegex_(),
|
||||
'name' => 'comparator string',
|
||||
'follows' => 'comparator'
|
||||
))
|
||||
));
|
||||
break;
|
||||
|
||||
case 'matchtype':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::Tag,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'regex' => $this->matchTypeRegex_(),
|
||||
'call' => 'matchTypeHook_',
|
||||
'name' => 'match type',
|
||||
'subArgs' => $this->makeArguments_($arg)
|
||||
));
|
||||
break;
|
||||
|
||||
case 'number':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::Number,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'regex' => $this->regex_($arg),
|
||||
'name' => $this->name_($arg),
|
||||
'follows' => $this->follows_($arg)
|
||||
));
|
||||
break;
|
||||
|
||||
case 'requirestrings':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::StringList,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'call' => 'setRequire_',
|
||||
'case' => 'adhere',
|
||||
'regex' => $this->requireStringsRegex_(),
|
||||
'name' => $this->name_($arg)
|
||||
));
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::String,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'regex' => $this->regex_($arg),
|
||||
'case' => $this->case_($arg),
|
||||
'name' => $this->name_($arg),
|
||||
'follows' => $this->follows_($arg)
|
||||
));
|
||||
break;
|
||||
|
||||
case 'stringlist':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::StringList,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'regex' => $this->regex_($arg),
|
||||
'case' => $this->case_($arg),
|
||||
'name' => $this->name_($arg),
|
||||
'follows' => $this->follows_($arg)
|
||||
));
|
||||
break;
|
||||
|
||||
case 'tag':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::Tag,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'regex' => $this->regex_($arg),
|
||||
'call' => 'tagHook_',
|
||||
'name' => $this->name_($arg),
|
||||
'subArgs' => $this->makeArguments_($arg->children()),
|
||||
'follows' => $this->follows_($arg)
|
||||
));
|
||||
break;
|
||||
|
||||
case 'test':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::Identifier,
|
||||
'occurrence' => $this->occurrence_($arg),
|
||||
'regex' => $this->testsRegex_(),
|
||||
'name' => $this->name_($arg),
|
||||
'subArgs' => $this->makeArguments_($arg->children())
|
||||
));
|
||||
break;
|
||||
|
||||
case 'testlist':
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::LeftParenthesis,
|
||||
'occurrence' => '1',
|
||||
'regex' => '\(',
|
||||
'name' => $this->name_($arg),
|
||||
'subArgs' => null
|
||||
));
|
||||
array_push($arguments, array(
|
||||
'type' => SieveToken::Identifier,
|
||||
'occurrence' => '+',
|
||||
'regex' => $this->testsRegex_(),
|
||||
'name' => $this->name_($arg),
|
||||
'subArgs' => $this->makeArguments_($arg->children())
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add argument(s) expected / allowed to appear next.
|
||||
* @param array $value
|
||||
*/
|
||||
protected function addArguments_($identifier, $subArgs)
|
||||
{
|
||||
for ($i = count($subArgs); $i > 0; $i--)
|
||||
{
|
||||
$arg = $subArgs[$i-1];
|
||||
if (preg_match('/^'. $arg['follows'] .'$/si', $identifier))
|
||||
array_unshift($this->arguments_, $arg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add dependency that is expected to be fullfilled when parsing
|
||||
* of the current command is {@see done}.
|
||||
* @param array $dependency
|
||||
*/
|
||||
protected function addDependency_($type, $name, $dependencies)
|
||||
{
|
||||
foreach ($dependencies as $d)
|
||||
{
|
||||
array_push($this->deps_, array(
|
||||
'o_type' => $type,
|
||||
'o_name' => $name,
|
||||
'type' => $d['type'],
|
||||
'name' => $d['name'],
|
||||
'regex' => $d['regex']
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
protected function invoke_($token, $func, $arg = array())
|
||||
{
|
||||
if (!is_array($arg))
|
||||
$arg = array($arg);
|
||||
|
||||
$err = call_user_func_array(array(&$this, $func), $arg);
|
||||
|
||||
if ($err)
|
||||
throw new SieveException($token, $err);
|
||||
}
|
||||
|
||||
protected function setRequire_($extension)
|
||||
{
|
||||
array_push(self::$requiredExtensions_, $extension);
|
||||
$this->registry_->activate($extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook function that is called after a address part match was found
|
||||
* in a command. The kind of address part is remembered in case it's
|
||||
* needed later {@see done}. For address parts from a extension
|
||||
* dependency information and valid values are looked up as well.
|
||||
* @param string $addresspart
|
||||
*/
|
||||
protected function addressPartHook_($addresspart)
|
||||
{
|
||||
$this->addressPart_ = $addresspart;
|
||||
$xml = $this->registry_->addresspart($this->addressPart_);
|
||||
|
||||
if (isset($xml))
|
||||
{
|
||||
// Add possible value and dependancy
|
||||
$this->addArguments_($this->addressPart_, $this->makeArguments_($xml));
|
||||
$this->addDependency_('address part', $this->addressPart_, $xml->requires);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook function that is called after a match type was found in a
|
||||
* command. The kind of match type is remembered in case it's
|
||||
* needed later {@see done}. For a match type from extensions
|
||||
* dependency information and valid values are looked up as well.
|
||||
* @param string $matchtype
|
||||
*/
|
||||
protected function matchTypeHook_($matchtype)
|
||||
{
|
||||
$this->matchType_ = $matchtype;
|
||||
$xml = $this->registry_->matchtype($this->matchType_);
|
||||
|
||||
if (isset($xml))
|
||||
{
|
||||
// Add possible value and dependancy
|
||||
$this->addArguments_($this->matchType_, $this->makeArguments_($xml));
|
||||
$this->addDependency_('match type', $this->matchType_, $xml->requires);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook function that is called after a comparator was found in
|
||||
* a command. The comparator is remembered in case it's needed for
|
||||
* comparsion later {@see done}. For a comparator from extensions
|
||||
* dependency information is looked up as well.
|
||||
* @param string $comparator
|
||||
*/
|
||||
protected function comparatorHook_($comparator)
|
||||
{
|
||||
$this->comparator_ = $comparator;
|
||||
$xml = $this->registry_->comparator($this->comparator_);
|
||||
|
||||
if (isset($xml))
|
||||
{
|
||||
// Add possible dependancy
|
||||
$this->addDependency_('comparator', $this->comparator_, $xml->requires);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook function that is called after a tag was found in
|
||||
* a command. The tag is remembered in case it's needed for
|
||||
* comparsion later {@see done}. For a tags from extensions
|
||||
* dependency information is looked up as well.
|
||||
* @param string $tag
|
||||
*/
|
||||
protected function tagHook_($tag)
|
||||
{
|
||||
array_push($this->tags_, $tag);
|
||||
$xml = $this->registry_->argument($tag);
|
||||
|
||||
// Add possible dependancies
|
||||
if (isset($xml))
|
||||
$this->addDependency_('tag', $tag, $xml->requires);
|
||||
}
|
||||
|
||||
protected function validType_($token)
|
||||
{
|
||||
foreach ($this->arguments_ as $arg)
|
||||
{
|
||||
if ($arg['occurrence'] == '0')
|
||||
{
|
||||
array_shift($this->arguments_);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->is($arg['type']))
|
||||
return;
|
||||
|
||||
// Is the argument required
|
||||
if ($arg['occurrence'] != '?' && $arg['occurrence'] != '*')
|
||||
throw new SieveException($token, $arg['type']);
|
||||
|
||||
array_shift($this->arguments_);
|
||||
}
|
||||
|
||||
// Check if command expects any (more) arguments
|
||||
if (empty($this->arguments_))
|
||||
throw new SieveException($token, $this->followupToken_);
|
||||
|
||||
throw new SieveException($token, 'unexpected '. SieveToken::typeString($token->type) .' '. $token->text);
|
||||
}
|
||||
|
||||
public function startStringList($token)
|
||||
{
|
||||
$this->validType_($token);
|
||||
$this->arguments_[0]['type'] = SieveToken::String;
|
||||
$this->arguments_[0]['occurrence'] = '+';
|
||||
}
|
||||
|
||||
public function continueStringList()
|
||||
{
|
||||
$this->arguments_[0]['occurrence'] = '+';
|
||||
}
|
||||
|
||||
public function endStringList()
|
||||
{
|
||||
array_shift($this->arguments_);
|
||||
}
|
||||
|
||||
public function validateToken($token)
|
||||
{
|
||||
// Make sure the argument has a valid type
|
||||
$this->validType_($token);
|
||||
|
||||
foreach ($this->arguments_ as &$arg)
|
||||
{
|
||||
// Build regular expression according to argument type
|
||||
switch ($arg['type'])
|
||||
{
|
||||
case SieveToken::String:
|
||||
case SieveToken::StringList:
|
||||
$regex = '/^(?:text:[^\n]*\n(?P<one>'. $arg['regex'] .')\.\r?\n?|"(?P<two>'. $arg['regex'] .')")$/'
|
||||
. ($arg['case'] == 'ignore' ? 'si' : 's');
|
||||
break;
|
||||
case SieveToken::Tag:
|
||||
$regex = '/^:(?P<one>'. $arg['regex'] .')$/si';
|
||||
break;
|
||||
default:
|
||||
$regex = '/^(?P<one>'. $arg['regex'] .')$/si';
|
||||
}
|
||||
|
||||
if (preg_match($regex, $token->text, $match))
|
||||
{
|
||||
$text = ($match['one'] ? $match['one'] : $match['two']);
|
||||
|
||||
// Add argument(s) that may now appear after this one
|
||||
if (isset($arg['subArgs']))
|
||||
$this->addArguments_($text, $arg['subArgs']);
|
||||
|
||||
// Call extra processing function if defined
|
||||
if (isset($arg['call']))
|
||||
$this->invoke_($token, $arg['call'], $text);
|
||||
|
||||
// Check if a possible value of this argument may occur
|
||||
if ($arg['occurrence'] == '?' || $arg['occurrence'] == '1')
|
||||
{
|
||||
$arg['occurrence'] = '0';
|
||||
}
|
||||
else if ($arg['occurrence'] == '+')
|
||||
{
|
||||
$arg['occurrence'] = '*';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($token->is($arg['type']) && $arg['occurrence'] == 1)
|
||||
{
|
||||
throw new SieveException($token,
|
||||
SieveToken::typeString($token->type) ." $token->text where ". $arg['name'] .' expected');
|
||||
}
|
||||
}
|
||||
|
||||
throw new SieveException($token, 'unexpected '. SieveToken::typeString($token->type) .' '. $token->text);
|
||||
}
|
||||
|
||||
public function done($token)
|
||||
{
|
||||
// Check if there are required arguments left
|
||||
foreach ($this->arguments_ as $arg)
|
||||
{
|
||||
if ($arg['occurrence'] == '+' || $arg['occurrence'] == '1')
|
||||
throw new SieveException($token, $arg['type']);
|
||||
}
|
||||
|
||||
// Check if the command depends on use of a certain tag
|
||||
foreach ($this->deps_ as $d)
|
||||
{
|
||||
switch ($d['type'])
|
||||
{
|
||||
case 'addresspart':
|
||||
$values = array($this->addressPart_);
|
||||
break;
|
||||
|
||||
case 'matchtype':
|
||||
$values = array($this->matchType_);
|
||||
break;
|
||||
|
||||
case 'comparator':
|
||||
$values = array($this->comparator_);
|
||||
break;
|
||||
|
||||
case 'tag':
|
||||
$values = $this->tags_;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($values as $value)
|
||||
{
|
||||
if (preg_match('/^'. $d['regex'] .'$/mi', $value))
|
||||
break 2;
|
||||
}
|
||||
|
||||
throw new SieveException($token,
|
||||
$d['o_type'] .' '. $d['o_name'] .' requires use of '. $d['type'] .' '. $d['name']);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php namespace Sieve;
|
||||
|
||||
include_once('SieveDumpable.php');
|
||||
|
||||
class SieveToken implements SieveDumpable
|
||||
{
|
||||
const Unknown = 0x0000;
|
||||
const ScriptEnd = 0x0001;
|
||||
const LeftBracket = 0x0002;
|
||||
const RightBracket = 0x0004;
|
||||
const BlockStart = 0x0008;
|
||||
const BlockEnd = 0x0010;
|
||||
const LeftParenthesis = 0x0020;
|
||||
const RightParenthesis = 0x0040;
|
||||
const Comma = 0x0080;
|
||||
const Semicolon = 0x0100;
|
||||
const Whitespace = 0x0200;
|
||||
const Tag = 0x0400;
|
||||
const QuotedString = 0x0800;
|
||||
const Number = 0x1000;
|
||||
const Comment = 0x2000;
|
||||
const MultilineString = 0x4000;
|
||||
const Identifier = 0x8000;
|
||||
|
||||
const String = 0x4800; // Quoted | Multiline
|
||||
const StringList = 0x4802; // Quoted | Multiline | LeftBracket
|
||||
const StringListSep = 0x0084; // Comma | RightBracket
|
||||
const Unparsed = 0x2200; // Comment | Whitespace
|
||||
const TestList = 0x8020; // Identifier | LeftParenthesis
|
||||
|
||||
public $type;
|
||||
public $text;
|
||||
public $line;
|
||||
|
||||
public function __construct($type, $text, $line)
|
||||
{
|
||||
$this->text = $text;
|
||||
$this->type = $type;
|
||||
$this->line = intval($line);
|
||||
}
|
||||
|
||||
public function dump()
|
||||
{
|
||||
return '<'. SieveToken::escape($this->text) .'> type:'. SieveToken::typeString($this->type) .' line:'. $this->line;
|
||||
}
|
||||
|
||||
public function text()
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function is($type)
|
||||
{
|
||||
return (bool)($this->type & $type);
|
||||
}
|
||||
|
||||
public static function typeString($type)
|
||||
{
|
||||
switch ($type)
|
||||
{
|
||||
case SieveToken::Identifier: return 'identifier';
|
||||
case SieveToken::Whitespace: return 'whitespace';
|
||||
case SieveToken::QuotedString: return 'quoted string';
|
||||
case SieveToken::Tag: return 'tag';
|
||||
case SieveToken::Semicolon: return 'semicolon';
|
||||
case SieveToken::LeftBracket: return 'left bracket';
|
||||
case SieveToken::RightBracket: return 'right bracket';
|
||||
case SieveToken::BlockStart: return 'block start';
|
||||
case SieveToken::BlockEnd: return 'block end';
|
||||
case SieveToken::LeftParenthesis: return 'left parenthesis';
|
||||
case SieveToken::RightParenthesis: return 'right parenthesis';
|
||||
case SieveToken::Comma: return 'comma';
|
||||
case SieveToken::Number: return 'number';
|
||||
case SieveToken::Comment: return 'comment';
|
||||
case SieveToken::MultilineString: return 'multiline string';
|
||||
case SieveToken::ScriptEnd: return 'script end';
|
||||
case SieveToken::String: return 'string';
|
||||
case SieveToken::StringList: return 'string list';
|
||||
default: return 'unknown token';
|
||||
}
|
||||
}
|
||||
|
||||
protected static $tr_ = array("\r" => '\r', "\n" => '\n', "\t" => '\t');
|
||||
public static function escape($val)
|
||||
{
|
||||
return strtr($val, self::$tr_);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?php namespace Sieve;
|
||||
|
||||
class SieveTree
|
||||
{
|
||||
protected $childs_;
|
||||
protected $parents_;
|
||||
protected $nodes_;
|
||||
protected $max_id_;
|
||||
protected $dump_;
|
||||
|
||||
public function __construct($name = 'tree')
|
||||
{
|
||||
$this->childs_ = array();
|
||||
$this->parents_ = array();
|
||||
$this->nodes_ = array();
|
||||
$this->max_id_ = 0;
|
||||
|
||||
$this->parents_[0] = null;
|
||||
$this->nodes_[0] = $name;
|
||||
}
|
||||
|
||||
public function addChild(SieveDumpable $child)
|
||||
{
|
||||
return $this->addChildTo($this->max_id_, $child);
|
||||
}
|
||||
|
||||
public function addChildTo($parent_id, SieveDumpable $child)
|
||||
{
|
||||
if (!is_int($parent_id)
|
||||
|| !isset($this->nodes_[$parent_id]))
|
||||
return null;
|
||||
|
||||
if (!isset($this->childs_[$parent_id]))
|
||||
$this->childs_[$parent_id] = array();
|
||||
|
||||
$child_id = ++$this->max_id_;
|
||||
$this->nodes_[$child_id] = $child;
|
||||
$this->parents_[$child_id] = $parent_id;
|
||||
array_push($this->childs_[$parent_id], $child_id);
|
||||
|
||||
return $child_id;
|
||||
}
|
||||
|
||||
public function getRoot()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getChilds($node_id)
|
||||
{
|
||||
if (!is_int($node_id)
|
||||
|| !isset($this->nodes_[$node_id]))
|
||||
return null;
|
||||
|
||||
if (!isset($this->childs_[$node_id]))
|
||||
return array();
|
||||
|
||||
return $this->childs_[$node_id];
|
||||
}
|
||||
|
||||
public function getNode($node_id)
|
||||
{
|
||||
if ($node_id == 0 || !is_int($node_id)
|
||||
|| !isset($this->nodes_[$node_id]))
|
||||
return null;
|
||||
|
||||
return $this->nodes_[$node_id];
|
||||
}
|
||||
|
||||
public function dump()
|
||||
{
|
||||
$this->dump_ = $this->nodes_[$this->getRoot()] ."\n";
|
||||
$this->dumpChilds_($this->getRoot(), ' ');
|
||||
return $this->dump_;
|
||||
}
|
||||
|
||||
protected function dumpChilds_($parent_id, $prefix)
|
||||
{
|
||||
if (!isset($this->childs_[$parent_id]))
|
||||
return;
|
||||
|
||||
$childs = $this->childs_[$parent_id];
|
||||
$last_child = count($childs);
|
||||
|
||||
for ($i=1; $i <= $last_child; ++$i)
|
||||
{
|
||||
$child_node = $this->nodes_[$childs[$i-1]];
|
||||
$infix = ($i == $last_child ? '`--- ' : '|--- ');
|
||||
$this->dump_ .= $prefix . $infix . $child_node->dump() . " (id:" . $childs[$i-1] . ")\n";
|
||||
|
||||
$next_prefix = $prefix . ($i == $last_child ? ' ' : '| ');
|
||||
$this->dumpChilds_($childs[$i-1], $next_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
public function getText()
|
||||
{
|
||||
$this->dump_ = '';
|
||||
$this->childText_($this->getRoot());
|
||||
return $this->dump_;
|
||||
}
|
||||
|
||||
protected function childText_($parent_id)
|
||||
{
|
||||
if (!isset($this->childs_[$parent_id]))
|
||||
return;
|
||||
|
||||
$childs = $this->childs_[$parent_id];
|
||||
|
||||
for ($i = 0; $i < count($childs); ++$i)
|
||||
{
|
||||
$child_node = $this->nodes_[$childs[$i]];
|
||||
$this->dump_ .= $child_node->text();
|
||||
$this->childText_($childs[$i]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="body">
|
||||
|
||||
<test name="body">
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="tag" name="body transform" regex="(raw|content|text)" occurrence="optional">
|
||||
<parameter type="stringlist" name="content types" follows="content" />
|
||||
</parameter>
|
||||
<parameter type="stringlist" name="key list" />
|
||||
</test>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="comparator-i;ascii-numeric">
|
||||
|
||||
<comparator name="i;ascii-numeric" />
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="copy">
|
||||
|
||||
<tagged-argument extends="(fileinto|redirect)">
|
||||
<parameter type="tag" name="copy" regex="copy" occurrence="optional" />
|
||||
</tagged-argument>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="date">
|
||||
|
||||
<test name="date">
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="tag" name="zone" regex="(zone|originalzone)" occurrence="optional">
|
||||
<parameter type="string" name="time-zone" follows="zone" />
|
||||
</parameter>
|
||||
<parameter type="string" name="header-name" />
|
||||
<parameter type="string" case="ignore" name="date-part"
|
||||
regex="(year|month|day|date|julian|hour|minute|second|time|iso8601|std11|zone|weekday)" />
|
||||
<parameter type="stringlist" name="key-list" />
|
||||
</test>
|
||||
|
||||
<test name="currentdate">
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="tag" name="zone" regex="zone" occurrence="optional">
|
||||
<parameter type="string" name="time-zone" />
|
||||
</parameter>
|
||||
<parameter type="string" case="ignore" name="date-part"
|
||||
regex="(year|month|day|date|julian|hour|minute|second|time|iso8601|std11|zone|weekday)" />
|
||||
<parameter type="stringlist" name="key-list" />
|
||||
</test>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="editheader">
|
||||
|
||||
<command name="addheader">
|
||||
<parameter type="tag" name="last" regex="last" occurrence="optional" />
|
||||
<parameter type="string" name="field name" />
|
||||
<parameter type="string" name="value" />
|
||||
</command>
|
||||
|
||||
<command name="deleteheader">
|
||||
<parameter type="tag" name="index" regex="index" occurrence="optional">
|
||||
<parameter type="number" name="field number" />
|
||||
<parameter type="tag" name="last" regex="last" occurrence="optional" />
|
||||
</parameter>
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="string" name="field name" />
|
||||
<parameter type="stringlist" name="value patterns" occurrence="optional" />
|
||||
</command>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="envelope">
|
||||
|
||||
<test name="envelope">
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="addresspart" occurrence="optional" />
|
||||
<parameter type="stringlist" name="envelope-part" />
|
||||
<parameter type="stringlist" name="key" />
|
||||
</test>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="environment">
|
||||
|
||||
<test name="environment">
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="string" name="name"
|
||||
regex="(domain|host|location|name|phase|remote-host|remote-ip|version|vnd\..+)" />
|
||||
<parameter type="stringlist" name="key-list" />
|
||||
</test>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="ereject">
|
||||
|
||||
<command name="ereject">
|
||||
|
||||
<parameter type="string" name="reason" />
|
||||
|
||||
</command>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="fileinto">
|
||||
|
||||
<command name="fileinto">
|
||||
<parameter type="string" name="folder" />
|
||||
</command>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="imap4flags">
|
||||
|
||||
<command name="setflag">
|
||||
<parameter type="stringlist" name="flag list" />
|
||||
</command>
|
||||
|
||||
<command name="addflag">
|
||||
<parameter type="stringlist" name="flag list" />
|
||||
</command>
|
||||
|
||||
<command name="removeflag">
|
||||
<parameter type="stringlist" name="flag list" />
|
||||
</command>
|
||||
|
||||
<test name="hasflag">
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="stringlist" name="flag list" />
|
||||
</test>
|
||||
|
||||
<tagged-argument extends="(fileinto|keep)">
|
||||
<parameter type="tag" name="flags" regex="flags" occurrence="optional">
|
||||
<parameter type="stringlist" name="flag list" />
|
||||
</parameter>
|
||||
</tagged-argument>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="imapflags">
|
||||
|
||||
<command name="mark" />
|
||||
|
||||
<command name="unmark" />
|
||||
|
||||
<command name="setflag">
|
||||
<parameter type="stringlist" name="flag list" />
|
||||
</command>
|
||||
|
||||
<command name="addflag">
|
||||
<parameter type="stringlist" name="flag list" />
|
||||
</command>
|
||||
|
||||
<command name="removeflag">
|
||||
<parameter type="stringlist" name="flag list" />
|
||||
</command>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="index">
|
||||
|
||||
<tagged-argument extends="(header|address|date)">
|
||||
<parameter type="tag" name="index" regex="index" occurrence="optional">
|
||||
<parameter type="number" name="field number" />
|
||||
</parameter>
|
||||
</tagged-argument>
|
||||
|
||||
<tagged-argument extends="(header|address|date)">
|
||||
<parameter type="tag" name="last" regex="last" occurrence="optional">
|
||||
<requires type="tag" name="index" regex="index" />
|
||||
</parameter>
|
||||
</tagged-argument>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="notify">
|
||||
|
||||
<command name="notify">
|
||||
<parameter type="tag" name="method" regex="method" occurrence="optional">
|
||||
<parameter type="string" name="method-name" />
|
||||
</parameter>
|
||||
|
||||
<parameter type="tag" name="id" regex="id" occurrence="optional">
|
||||
<parameter type="string" name="message-id" />
|
||||
</parameter>
|
||||
|
||||
<parameter type="tag" name="priority" regex="(low|normal|high)" occurrence="optional" />
|
||||
|
||||
<parameter type="tag" name="message" regex="message" occurrence="optional">
|
||||
<parameter type="string" name="message-text" />
|
||||
</parameter>
|
||||
</command>
|
||||
|
||||
<command name="denotify">
|
||||
<parameter type="matchtype" occurrence="optional">
|
||||
<parameter type="string" name="message-id" />
|
||||
</parameter>
|
||||
|
||||
<parameter type="tag" name="priority" regex="(low|normal|high)" occurrence="optional" />
|
||||
</command>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="regex">
|
||||
|
||||
<matchtype name="regex" />
|
||||
|
||||
<tagged-argument extends="set">
|
||||
<parameter type="tag" name="modifier" regex="quoteregex" occurrence="optional" />
|
||||
</tagged-argument>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="reject">
|
||||
|
||||
<command name="reject">
|
||||
|
||||
<parameter type="string" name="reason" />
|
||||
|
||||
</command>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="relational">
|
||||
|
||||
<matchtype name="count">
|
||||
<requires type="comparator" name="i;ascii-numeric" regex="i;ascii-numeric" />
|
||||
<parameter type="string" name="relation string" regex="(lt|le|eq|ge|gt|ne)" />
|
||||
</matchtype>
|
||||
|
||||
<matchtype name="value">
|
||||
<parameter type="string" name="relation string" regex="(lt|le|eq|ge|gt|ne)" />
|
||||
</matchtype>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="spamtest">
|
||||
|
||||
<test name="spamtest">
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="string" name="value" />
|
||||
</test>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="spamtestplus">
|
||||
|
||||
<test name="spamtest" overrides="true">
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="tag" name="percent" regex="percent" occurrence="optional" />
|
||||
<parameter type="string" name="value" />
|
||||
</test>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="subaddress">
|
||||
|
||||
<addresspart name="user" />
|
||||
<addresspart name="detail" />
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="vacation">
|
||||
|
||||
<command name="vacation">
|
||||
<parameter type="tag" name="days" occurrence="optional" regex="days">
|
||||
<parameter type="number" name="period" />
|
||||
</parameter>
|
||||
|
||||
<parameter type="tag" name="addresses" occurrence="optional" regex="addresses">
|
||||
<parameter type="stringlist" name="address strings" />
|
||||
</parameter>
|
||||
|
||||
<parameter type="tag" name="subject" occurrence="optional" regex="subject">
|
||||
<parameter type="string" name="subject string" />
|
||||
</parameter>
|
||||
|
||||
<parameter type="tag" name="from" occurrence="optional" regex="from">
|
||||
<parameter type="string" name="from string" />
|
||||
</parameter>
|
||||
|
||||
<parameter type="tag" name="handle" occurrence="optional" regex="handle">
|
||||
<parameter type="string" name="handle string" />
|
||||
</parameter>
|
||||
|
||||
<parameter type="tag" name="mime" occurrence="optional" regex="mime" />
|
||||
|
||||
<parameter type="string" name="reason" />
|
||||
</command>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="variables">
|
||||
|
||||
<command name="set">
|
||||
<parameter type="tag" name="modifier" regex="(lower|upper)" occurrence="optional" />
|
||||
<parameter type="tag" name="modifier" regex="(lower|upper)first" occurrence="optional" />
|
||||
<parameter type="tag" name="modifier" regex="quotewildcard" occurrence="optional" />
|
||||
<parameter type="tag" name="modifier" regex="length" occurrence="optional" />
|
||||
<parameter type="string" name="name" regex="[[:alpha:]_][[:alnum:]_]*" />
|
||||
<parameter type="string" name="value" />
|
||||
</command>
|
||||
|
||||
<test name="string">
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="stringlist" name="source" />
|
||||
<parameter type="stringlist" name="key list" />
|
||||
</test>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<extension name="virustest">
|
||||
|
||||
<test name="virustest">
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="string" name="value" />
|
||||
</test>
|
||||
|
||||
</extension>
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version='1.0' standalone='yes'?>
|
||||
|
||||
<keywords>
|
||||
|
||||
<matchtype name="is" />
|
||||
<matchtype name="contains" />
|
||||
<matchtype name="matches" />
|
||||
<matchtype name="value">
|
||||
<parameter type="string" name="operator" regex="(gt|ge|eq|le|lt)" />
|
||||
</matchtype>
|
||||
|
||||
|
||||
<comparator name="i;octet" />
|
||||
<comparator name="i;ascii-casemap" />
|
||||
<comparator name="i;unicode-casemap" />
|
||||
|
||||
<addresspart name="all" />
|
||||
<addresspart name="localpart" />
|
||||
<addresspart name="domain" />
|
||||
|
||||
|
||||
<command name="discard" />
|
||||
|
||||
<command name="elsif">
|
||||
<parameter type="test" name="test command" />
|
||||
<parameter type="block" />
|
||||
</command>
|
||||
|
||||
<command name="else">
|
||||
<parameter type="block" />
|
||||
</command>
|
||||
|
||||
<command name="if">
|
||||
<parameter type="test" name="test command" />
|
||||
<parameter type="block" />
|
||||
</command>
|
||||
|
||||
<command name="keep" />
|
||||
|
||||
<command name="redirect">
|
||||
<parameter type="string" name="address string" />
|
||||
</command>
|
||||
|
||||
<command name="require">
|
||||
<parameter type="requirestrings" name="require string" />
|
||||
</command>
|
||||
|
||||
<command name="stop" />
|
||||
|
||||
|
||||
<test name="address">
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="addresspart" occurrence="optional" />
|
||||
<parameter type="stringlist" name="header list" />
|
||||
<parameter type="stringlist" name="key list" />
|
||||
</test>
|
||||
|
||||
<test name="allof">
|
||||
<parameter type="testlist" name="test" />
|
||||
</test>
|
||||
|
||||
<test name="anyof">
|
||||
<parameter type="testlist" name="test" />
|
||||
</test>
|
||||
|
||||
<test name="exists">
|
||||
<parameter type="stringlist" name="header names" />
|
||||
</test>
|
||||
|
||||
<test name="false" />
|
||||
|
||||
<test name="header">
|
||||
<parameter type="matchtype" occurrence="optional" />
|
||||
<parameter type="comparator" occurrence="optional" />
|
||||
<parameter type="stringlist" name="header names" />
|
||||
<parameter type="stringlist" name="key list" />
|
||||
</test>
|
||||
|
||||
<test name="not">
|
||||
<parameter type="test" />
|
||||
</test>
|
||||
|
||||
<test name="size">
|
||||
<parameter type="tag" regex="(over|under)" />
|
||||
<parameter type="number" name="limit" />
|
||||
</test>
|
||||
|
||||
<test name="true" />
|
||||
|
||||
</keywords>
|
|
@ -16,6 +16,9 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/Yubico.php';
|
|||
// Autoload composer
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/vendor/autoload.php';
|
||||
|
||||
// Load Sieve
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/sieve/SieveParser.php';
|
||||
|
||||
// U2F API + T/HOTP API
|
||||
$u2f = new u2flib_server\U2F('https://' . $_SERVER['HTTP_HOST']);
|
||||
$tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL);
|
||||
|
@ -70,6 +73,7 @@ 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.relayhost.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fail2ban.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.docker.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/init_db.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.inc.php';
|
||||
init_db_schema();
|
||||
|
|
|
@ -62,6 +62,8 @@ if (isset($_POST["logout"])) {
|
|||
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
|
||||
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
|
||||
unset($_SESSION["dual-login"]);
|
||||
header("Location: /mailbox.php");
|
||||
exit();
|
||||
}
|
||||
else {
|
||||
session_regenerate_id(true);
|
||||
|
|
|
@ -104,6 +104,9 @@ $MAILCOW_APPS = array(
|
|||
// Rows until pagination begins
|
||||
$PAGINATION_SIZE = 10;
|
||||
|
||||
// Default number of rows/lines to display (log table)
|
||||
$LOG_LINES = 100;
|
||||
|
||||
// Rows until pagination begins (log table)
|
||||
$LOG_PAGINATION_SIZE = 30;
|
||||
|
||||
|
|
|
@ -1,125 +1,10 @@
|
|||
var Base64 = {
|
||||
_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
encode: function(e) {
|
||||
var t = "";
|
||||
var n, r, i, s, o, u, a;
|
||||
var f = 0;
|
||||
e = Base64._utf8_encode(e);
|
||||
while (f < e.length) {
|
||||
n = e.charCodeAt(f++);
|
||||
r = e.charCodeAt(f++);
|
||||
i = e.charCodeAt(f++);
|
||||
s = n >> 2;
|
||||
o = (n & 3) << 4 | r >> 4;
|
||||
u = (r & 15) << 2 | i >> 6;
|
||||
a = i & 63;
|
||||
if (isNaN(r)) {
|
||||
u = a = 64
|
||||
} else if (isNaN(i)) {
|
||||
a = 64
|
||||
}
|
||||
t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) +
|
||||
this._keyStr.charAt(u) + this._keyStr.charAt(a)
|
||||
}
|
||||
return t
|
||||
},
|
||||
decode: function(e) {
|
||||
var t = "";
|
||||
var n, r, i;
|
||||
var s, o, u, a;
|
||||
var f = 0;
|
||||
e = e.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
while (f < e.length) {
|
||||
s = this._keyStr.indexOf(e.charAt(f++));
|
||||
o = this._keyStr.indexOf(e.charAt(f++));
|
||||
u = this._keyStr.indexOf(e.charAt(f++));
|
||||
a = this._keyStr.indexOf(e.charAt(f++));
|
||||
n = s << 2 | o >> 4;
|
||||
r = (o & 15) << 4 | u >> 2;
|
||||
i = (u & 3) << 6 | a;
|
||||
t = t + String.fromCharCode(n);
|
||||
if (u != 64) {
|
||||
t = t + String.fromCharCode(r)
|
||||
}
|
||||
if (a != 64) {
|
||||
t = t + String.fromCharCode(i)
|
||||
}
|
||||
}
|
||||
t = Base64._utf8_decode(t);
|
||||
return t
|
||||
},
|
||||
_utf8_encode: function(e) {
|
||||
e = e.replace(/\r\n/g, "\n");
|
||||
var t = "";
|
||||
for (var n = 0; n < e.length; n++) {
|
||||
var r = e.charCodeAt(n);
|
||||
if (r < 128) {
|
||||
t += String.fromCharCode(r)
|
||||
} else if (r > 127 && r < 2048) {
|
||||
t += String.fromCharCode(r >> 6 | 192);
|
||||
t += String.fromCharCode(r & 63 | 128)
|
||||
} else {
|
||||
t += String.fromCharCode(r >> 12 | 224);
|
||||
t += String.fromCharCode(r >> 6 & 63 | 128);
|
||||
t += String.fromCharCode(r & 63 | 128)
|
||||
}
|
||||
}
|
||||
return t
|
||||
},
|
||||
_utf8_decode: function(e) {
|
||||
var t = "";
|
||||
var n = 0;
|
||||
var r = c1 = c2 = 0;
|
||||
while (n < e.length) {
|
||||
r = e.charCodeAt(n);
|
||||
if (r < 128) {
|
||||
t += String.fromCharCode(r);
|
||||
n++
|
||||
} else if (r > 191 && r < 224) {
|
||||
c2 = e.charCodeAt(n + 1);
|
||||
t += String.fromCharCode((r & 31) << 6 | c2 & 63);
|
||||
n += 2
|
||||
} else {
|
||||
c2 = e.charCodeAt(n + 1);
|
||||
c3 = e.charCodeAt(n + 2);
|
||||
t += String.fromCharCode((r & 15) << 12 | (c2 & 63) <<
|
||||
6 | c3 & 63);
|
||||
n += 3
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
// 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(string) {
|
||||
return String(string).replace(/[&<>"'`=\/]/g, function (s) {
|
||||
return entityMap[s];
|
||||
});
|
||||
}
|
||||
function humanFileSize(bytes) {
|
||||
if(Math.abs(bytes) < 1024) {
|
||||
return bytes + ' B';
|
||||
}
|
||||
var units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
|
||||
var u = -1;
|
||||
do {
|
||||
bytes /= 1024;
|
||||
++u;
|
||||
} while(Math.abs(bytes) >= 1024 && u < units.length - 1);
|
||||
return bytes.toFixed(1)+' '+units[u];
|
||||
}
|
||||
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();
|
||||
|
@ -148,54 +33,6 @@ jQuery(function($){
|
|||
e.preventDefault();
|
||||
$('#import_dkim_arrow').toggleClass("animation");
|
||||
});
|
||||
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/1000',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw postfix log table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.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>';
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
"empty": lang.empty,
|
||||
"paging": {
|
||||
"enabled": true,
|
||||
"limit": 5,
|
||||
"size": log_pagination_size
|
||||
},
|
||||
"filtering": {
|
||||
"enabled": true,
|
||||
"position": "left",
|
||||
"placeholder": lang.filter_table
|
||||
},
|
||||
"sorting": {
|
||||
"enabled": true
|
||||
}
|
||||
});
|
||||
}
|
||||
function draw_autodiscover_logs() {
|
||||
ft_autodiscover_logs = FooTable.init('#autodiscover_log', {
|
||||
"columns": [
|
||||
|
@ -212,33 +49,53 @@ jQuery(function($){
|
|||
console.log('Cannot draw autodiscover log table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.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>';
|
||||
}
|
||||
});
|
||||
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
|
||||
"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;
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -251,43 +108,27 @@ jQuery(function($){
|
|||
],
|
||||
"rows": $.ajax({
|
||||
dataType: 'json',
|
||||
url: '/api/v1/get/logs/fail2ban/1000',
|
||||
url: '/api/v1/get/logs/fail2ban',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw fail2ban log table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.each(data, function (i, item) {
|
||||
var danger_class = ["emerg", "alert", "crit", "err"];
|
||||
var warning_class = ["warning", "warn"];
|
||||
var info_class = ["notice", "info", "debug"];
|
||||
item.message = escapeHtml(item.message);
|
||||
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 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
|
||||
"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;
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -300,48 +141,32 @@ jQuery(function($){
|
|||
],
|
||||
"rows": $.ajax({
|
||||
dataType: 'json',
|
||||
url: '/api/v1/get/logs/sogo/1000',
|
||||
url: '/api/v1/get/logs/sogo',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw sogo log table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.each(data, function (i, item) {
|
||||
var danger_class = ["emerg", "alert", "crit", "err"];
|
||||
var warning_class = ["warning", "warn"];
|
||||
var info_class = ["notice", "info", "debug"];
|
||||
item.message = escapeHtml(item.message);
|
||||
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 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
|
||||
"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_postfix_logs = FooTable.init('#dovecot_log', {
|
||||
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"}},
|
||||
|
@ -349,43 +174,27 @@ jQuery(function($){
|
|||
],
|
||||
"rows": $.ajax({
|
||||
dataType: 'json',
|
||||
url: '/api/v1/get/logs/dovecot/1000',
|
||||
url: '/api/v1/get/logs/dovecot',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw dovecot log table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.each(data, function (i, item) {
|
||||
var danger_class = ["emerg", "alert", "crit", "err"];
|
||||
var warning_class = ["warning", "warn"];
|
||||
var info_class = ["notice", "info", "debug"];
|
||||
item.message = escapeHtml(item.message);
|
||||
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 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
|
||||
"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;
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -407,30 +216,14 @@ jQuery(function($){
|
|||
console.log('Cannot draw domain admin table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.each(data, function (i, item) {
|
||||
item.chkbox = '<input type="checkbox" data-id="domain_admins" name="multi_select" value="' + item.username + '" />';
|
||||
item.action = '<div class="btn-group">' +
|
||||
'<a href="/edit.php?domainadmin=' + encodeURI(item.username) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</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>';
|
||||
});
|
||||
return process_table_data(data, 'domainadminstable');
|
||||
}
|
||||
}),
|
||||
"empty": lang.empty,
|
||||
"paging": {
|
||||
"enabled": true,
|
||||
"limit": 5,
|
||||
"size": log_pagination_size
|
||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table
|
||||
},
|
||||
"filtering": {
|
||||
"enabled": true,
|
||||
"position": "left",
|
||||
"connectors": false,
|
||||
"placeholder": lang.filter_table
|
||||
},
|
||||
"sorting": {
|
||||
"enabled": true
|
||||
}
|
||||
"sorting": {"enabled": true}
|
||||
});
|
||||
}
|
||||
function draw_fwd_hosts() {
|
||||
|
@ -450,33 +243,16 @@ jQuery(function($){
|
|||
console.log('Cannot draw forwarding hosts table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.each(data, function (i, item) {
|
||||
item.action = '<div class="btn-group">' +
|
||||
'<a href="#" id="delete_selected" data-id="single-fwdhost" data-api-url="delete/fwdhost" data-item="' + encodeURI(item.host) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
||||
'</div>';
|
||||
if (item.keep_spam == "yes") {
|
||||
item.keep_spam = lang.no;
|
||||
}
|
||||
else {
|
||||
item.keep_spam = lang.yes;
|
||||
}
|
||||
item.chkbox = '<input type="checkbox" data-id="fwdhosts" name="multi_select" value="' + item.host + '" />';
|
||||
});
|
||||
return process_table_data(data, 'forwardinghoststable');
|
||||
}
|
||||
}),
|
||||
"empty": lang.empty,
|
||||
"paging": {
|
||||
"enabled": true,
|
||||
"limit": 5,
|
||||
"size": log_pagination_size
|
||||
},
|
||||
"sorting": {
|
||||
"enabled": true
|
||||
}
|
||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||
"sorting": {"enabled": true}
|
||||
});
|
||||
}
|
||||
function draw_relayhosts() {
|
||||
ft_forwardinghoststable = FooTable.init('#relayhoststable', {
|
||||
ft_relayhoststable = FooTable.init('#relayhoststable', {
|
||||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"name":"id","type":"text","title":"ID","style":{"width":"50px"}},
|
||||
|
@ -494,115 +270,30 @@ jQuery(function($){
|
|||
console.log('Cannot draw forwarding hosts table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.each(data, function (i, item) {
|
||||
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="/edit.php?relayhost=' + encodeURI(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-rlshost" data-api-url="delete/relayhost" 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="rlyhosts" name="multi_select" value="' + item.id + '" />';
|
||||
});
|
||||
return process_table_data(data, 'relayhoststable');
|
||||
}
|
||||
}),
|
||||
"empty": lang.empty,
|
||||
"paging": {
|
||||
"enabled": true,
|
||||
"limit": 5,
|
||||
"size": log_pagination_size
|
||||
},
|
||||
"sorting": {
|
||||
"enabled": true
|
||||
}
|
||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||
"sorting": {"enabled": true}
|
||||
});
|
||||
}
|
||||
function draw_rspamd_history() {
|
||||
ft_postfix_logs = 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
|
||||
}
|
||||
}],
|
||||
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',
|
||||
|
@ -611,81 +302,146 @@ jQuery(function($){
|
|||
console.log('Cannot draw rspamd history table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.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";
|
||||
}
|
||||
});
|
||||
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
|
||||
"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 == 'relayhoststable') {
|
||||
$.each(data, function (i, item) {
|
||||
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="/edit.php?relayhost=' + encodeURI(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-rlshost" data-api-url="delete/relayhost" 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="rlyhosts" name="multi_select" value="' + item.id + '" />';
|
||||
});
|
||||
} else if (table == 'forwardinghoststable') {
|
||||
$.each(data, function (i, item) {
|
||||
item.action = '<div class="btn-group">' +
|
||||
'<a href="#" id="delete_selected" data-id="single-fwdhost" data-api-url="delete/fwdhost" data-item="' + encodeURI(item.host) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
||||
'</div>';
|
||||
if (item.keep_spam == "yes") {
|
||||
item.keep_spam = lang.no;
|
||||
}
|
||||
else {
|
||||
item.keep_spam = lang.yes;
|
||||
}
|
||||
item.chkbox = '<input type="checkbox" data-id="fwdhosts" name="multi_select" value="' + item.host + '" />';
|
||||
});
|
||||
} else if (table == 'domainadminstable') {
|
||||
$.each(data, function (i, item) {
|
||||
item.chkbox = '<input type="checkbox" data-id="domain_admins" name="multi_select" value="' + item.username + '" />';
|
||||
item.action = '<div class="btn-group">' +
|
||||
'<a href="/edit.php?domainadmin=' + encodeURI(item.username) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</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>';
|
||||
});
|
||||
} 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
|
||||
};
|
||||
// Initial table drawings
|
||||
draw_postfix_logs();
|
||||
draw_autodiscover_logs();
|
||||
draw_dovecot_logs();
|
||||
|
@ -695,7 +451,7 @@ jQuery(function($){
|
|||
draw_fwd_hosts();
|
||||
draw_relayhosts();
|
||||
draw_rspamd_history();
|
||||
|
||||
// Relayhost
|
||||
$('#testRelayhostModal').on('show.bs.modal', function (e) {
|
||||
$('#test_relayhost_result').text("-");
|
||||
button = $(e.relatedTarget)
|
||||
|
@ -703,16 +459,6 @@ jQuery(function($){
|
|||
$('#relayhost_id').val(button.data('relayhost-id'));
|
||||
}
|
||||
})
|
||||
|
||||
$('#showDKIMprivKey').on('show.bs.modal', function (e) {
|
||||
$('#priv_key_pre').text("-");
|
||||
p_related = $(e.relatedTarget)
|
||||
if (p_related != null) {
|
||||
var decoded_key = Base64.decode((p_related.data('priv-key')));
|
||||
$('#priv_key_pre').text(decoded_key);
|
||||
}
|
||||
})
|
||||
|
||||
$('#test_relayhost').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
prev = $('#test_relayhost').text();
|
||||
|
@ -720,7 +466,7 @@ jQuery(function($){
|
|||
$(this).html('<span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ');
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'inc/relay_check.php',
|
||||
url: 'inc/ajax/relay_check.php',
|
||||
dataType: 'text',
|
||||
data: $('#test_relayhost_form').serialize(),
|
||||
complete: function (data) {
|
||||
|
@ -730,7 +476,41 @@ jQuery(function($){
|
|||
}
|
||||
});
|
||||
})
|
||||
// DKIM private key modal
|
||||
$('#showDKIMprivKey').on('show.bs.modal', function (e) {
|
||||
$('#priv_key_pre').text("-");
|
||||
p_related = $(e.relatedTarget)
|
||||
if (p_related != null) {
|
||||
var decoded_key = Base64.decode((p_related.data('priv-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
|
||||
function add_table_row(table_id) {
|
||||
var row = $('<tr />');
|
||||
cols = '<td><input class="input-sm form-control" data-id="app_links" type="text" name="app" required></td>';
|
||||
|
@ -739,17 +519,14 @@ jQuery(function($){
|
|||
row.append(cols);
|
||||
table_id.append(row);
|
||||
}
|
||||
|
||||
$('#app_link_table').on('click', 'tr a', function (e) {
|
||||
e.preventDefault();
|
||||
$(this).parents('tr').remove();
|
||||
});
|
||||
|
||||
$('#add_app_link_row').click(function() {
|
||||
add_table_row($('#app_link_table'));
|
||||
});
|
||||
});
|
||||
|
||||
$(window).load(function(){
|
||||
initial_width = $("#sidebar-admin").width();
|
||||
$("#scrollbox").css("width", initial_width);
|
||||
|
@ -761,13 +538,10 @@ $(window).load(function(){
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
function resizeScrollbox() {
|
||||
on_resize_width = $("#sidebar-admin").width();
|
||||
$("#scrollbox").removeAttr("style");
|
||||
$("#scrollbox").css("width", on_resize_width);
|
||||
}
|
||||
|
||||
$(window).on('resize', resizeScrollbox);
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', resizeScrollbox);
|
||||
|
||||
|
|
|
@ -61,6 +61,11 @@ $(document).ready(function() {
|
|||
var id = $(this).data('id');
|
||||
var api_url = $(this).data('api-url');
|
||||
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 element #edit_selected is in a form with the same data-id as the button,
|
||||
// we merge all input fields by {"name":"value"} into api-attr
|
||||
if ($(this).closest("form").data('id') == id) {
|
||||
|
@ -74,6 +79,21 @@ $(document).ready(function() {
|
|||
$(this).removeClass('inputMissingAttr');
|
||||
}
|
||||
}
|
||||
if ($(this).attr("max")) {
|
||||
if ($(this).val() > $(this).attr("max")) {
|
||||
invalid = true;
|
||||
$(this).addClass('inputMissingAttr');
|
||||
} else {
|
||||
if ($(this).attr("min")) {
|
||||
if ($(this).val() < $(this).attr("min")) {
|
||||
invalid = true;
|
||||
$(this).addClass('inputMissingAttr');
|
||||
} else {
|
||||
$(this).removeClass('inputMissingAttr');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!req_empty) {
|
||||
var attr_to_merge = $(this).closest("form").serializeObject();
|
||||
|
@ -106,10 +126,10 @@ $(document).ready(function() {
|
|||
jsonp: false,
|
||||
complete: function(data) {
|
||||
var response = (data.responseText);
|
||||
// alert(response);
|
||||
// console.log(reponse.type);
|
||||
// console.log(reponse.msg);
|
||||
window.location = window.location.href.split("#")[0];
|
||||
response_obj = JSON.parse(response);
|
||||
if (api_reload_window === true) {
|
||||
window.location = window.location.href.split("#")[0];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -124,18 +144,33 @@ $(document).ready(function() {
|
|||
// 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
|
||||
if ($(this).closest("form").data('id') == id) {
|
||||
var req_empty = false;
|
||||
var invalid = false;
|
||||
$(this).closest("form").find('select, textarea, input').each(function() {
|
||||
if ($(this).prop('required')) {
|
||||
if (!$(this).val() && $(this).prop('disabled') === false) {
|
||||
req_empty = true;
|
||||
invalid = true;
|
||||
$(this).addClass('inputMissingAttr');
|
||||
} else {
|
||||
$(this).removeClass('inputMissingAttr');
|
||||
}
|
||||
}
|
||||
if ($(this).attr("max")) {
|
||||
if ($(this).val() > $(this).attr("max")) {
|
||||
invalid = true;
|
||||
$(this).addClass('inputMissingAttr');
|
||||
} else {
|
||||
if ($(this).attr("min")) {
|
||||
if ($(this).val() < $(this).attr("min")) {
|
||||
invalid = true;
|
||||
$(this).addClass('inputMissingAttr');
|
||||
} else {
|
||||
$(this).removeClass('inputMissingAttr');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!req_empty) {
|
||||
if (!invalid) {
|
||||
var attr_to_merge = $(this).closest("form").serializeObject();
|
||||
var api_attr = $.extend(api_attr, attr_to_merge)
|
||||
} else {
|
||||
|
|
|
@ -10,6 +10,7 @@ $(document).ready(function() {
|
|||
$("#textarea_alias_goto").removeAttr('disabled');
|
||||
}
|
||||
});
|
||||
$("#script_data").numberedtextarea({allowTabChar: true});
|
||||
});
|
||||
|
||||
jQuery(function($){
|
||||
|
|
|
@ -40,10 +40,61 @@ $(document).ready(function() {
|
|||
});
|
||||
|
||||
// Log modal
|
||||
$('#logModal').on('show.bs.modal', function(e) {
|
||||
var logText = $(e.relatedTarget).data('log-text');
|
||||
$(e.currentTarget).find('#logText').html('<pre style="background:none;font-size:11px;line-height:1.1;border:0px">' + logText + '</pre>');
|
||||
$('#syncjobLogModal').on('show.bs.modal', function(e) {
|
||||
var syncjob_id = $(e.relatedTarget).data('syncjob-id');
|
||||
$.ajax({
|
||||
url: '/inc/ajax/syncjob_logs.php',
|
||||
data: { id: syncjob_id },
|
||||
dataType: 'text',
|
||||
success: function(data){
|
||||
$(e.currentTarget).find('#logText').text(data);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
$(e.currentTarget).find('#logText').text(xhr.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Sieve data modal
|
||||
$('#sieveDataModal').on('show.bs.modal', function(e) {
|
||||
var sieveScript = $(e.relatedTarget).data('sieve-script');
|
||||
$(e.currentTarget).find('#sieveDataText').html('<pre style="font-size:14px;line-height:1.1">' + sieveScript + '</pre>');
|
||||
});
|
||||
|
||||
// Set line numbers for textarea
|
||||
$("#script_data").numberedtextarea({allowTabChar: true});
|
||||
// Disable submit button on script change
|
||||
$('#script_data').on('keyup', function() {
|
||||
$('#add_filter_btns > #add_item').attr({"disabled": true});
|
||||
$('#validation_msg').html('-');
|
||||
});
|
||||
|
||||
// Validate script data
|
||||
$("#validate_sieve").click(function( event ) {
|
||||
event.preventDefault();
|
||||
var script = $('#script_data').val();
|
||||
$.ajax({
|
||||
dataType: 'jsonp',
|
||||
url: "/inc/ajax/sieve_validation.php",
|
||||
type: "get",
|
||||
data: { script: script },
|
||||
complete: function(data) {
|
||||
var response = (data.responseText);
|
||||
response_obj = JSON.parse(response);
|
||||
if (response_obj.type == "success") {
|
||||
$('#add_filter_btns > #add_item').attr({"disabled": false});
|
||||
}
|
||||
mailcow_alert_box(response_obj.msg, response_obj.type);
|
||||
},
|
||||
});
|
||||
});
|
||||
// $(document).on('DOMNodeInserted', '#prefilter_table', function () {
|
||||
// $("#active-script").closest('td').css('background-color','#b0f0a0');
|
||||
// $("#inactive-script").closest('td').css('background-color','#b0f0a0');
|
||||
// });
|
||||
|
||||
|
||||
|
||||
});
|
||||
jQuery(function($){
|
||||
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
|
||||
|
@ -87,7 +138,7 @@ jQuery(function($){
|
|||
function draw_domain_table() {
|
||||
ft_domain_table = FooTable.init('#domain_table', {
|
||||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"sorted": true,"name":"domain_name","title":lang.domain,"style":{"width":"250px"}},
|
||||
{"name":"aliases","title":lang.aliases,"breakpoints":"xs sm"},
|
||||
{"name":"mailboxes","title":lang.mailboxes},
|
||||
|
@ -152,7 +203,7 @@ jQuery(function($){
|
|||
function draw_mailbox_table() {
|
||||
ft_mailbox_table = FooTable.init('#mailbox_table', {
|
||||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"sorted": true,"name":"username","style":{"word-break":"break-all","min-width":"120px"},"title":lang.username},
|
||||
{"name":"name","title":lang.fname,"style":{"word-break":"break-all","min-width":"120px"},"breakpoints":"xs sm"},
|
||||
{"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
|
||||
|
@ -222,7 +273,7 @@ jQuery(function($){
|
|||
function draw_resource_table() {
|
||||
ft_resource_table = FooTable.init('#resource_table', {
|
||||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"sorted": true,"name":"description","title":lang.description,"style":{"width":"250px"}},
|
||||
{"name":"kind","title":lang.kind},
|
||||
{"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
|
||||
|
@ -267,7 +318,7 @@ jQuery(function($){
|
|||
function draw_alias_table() {
|
||||
ft_alias_table = FooTable.init('#alias_table', {
|
||||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"sorted": true,"name":"address","title":lang.alias,"style":{"width":"250px"}},
|
||||
{"name":"goto","title":lang.target_address},
|
||||
{"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
|
||||
|
@ -320,7 +371,7 @@ jQuery(function($){
|
|||
function draw_aliasdomain_table() {
|
||||
ft_aliasdomain_table = FooTable.init('#aliasdomain_table', {
|
||||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"sorted": true,"name":"alias_domain","title":lang.alias,"style":{"width":"250px"}},
|
||||
{"name":"target_domain","title":lang.target_domain},
|
||||
{"name":"active","filterable": false,"style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
|
||||
|
@ -363,27 +414,28 @@ jQuery(function($){
|
|||
function draw_sync_job_table() {
|
||||
ft_syncjob_table = FooTable.init('#sync_job_table', {
|
||||
"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"}},
|
||||
{"name":"user2","title":lang.owner},
|
||||
{"name":"server_w_port","title":"Server","breakpoints":"xs"},
|
||||
{"name":"mins_interval","title":lang.mins_interval,"breakpoints":"all"},
|
||||
{"name":"last_run","title":lang.last_run,"breakpoints":"all"},
|
||||
{"name":"log","title":"Log"},
|
||||
{"name":"active","filterable": false,"style":{"maxWidth":"50px","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":"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/syncjobs/all',
|
||||
url: '/api/v1/get/syncjobs/all/no_log',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw sync job table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.each(data, function (i, item) {
|
||||
item.log = '<a href="#logModal" data-toggle="modal" data-log-text="' + escapeHtml(item.returned_text) + '">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>'
|
||||
item.server_w_port = item.user1 + '@' + item.host1 + ':' + item.port1;
|
||||
item.action = '<div class="btn-group">' +
|
||||
|
@ -391,6 +443,14 @@ jQuery(function($){
|
|||
'<a href="#" id="delete_selected" data-id="single-syncjob" data-api-url="delete/syncjob" 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="syncjob" name="multi_select" value="' + item.id + '" />';
|
||||
if (item.is_running == 1) {
|
||||
item.is_running = '<span id="active-script" class="label label-success">' + lang.running + '</span>';
|
||||
} else {
|
||||
item.is_running = '<span id="inactive-script" class="label label-warning">' + lang.waiting + '</span>';
|
||||
}
|
||||
if (!item.last_run > 0) {
|
||||
item.last_run = lang.waiting;
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
@ -399,16 +459,76 @@ jQuery(function($){
|
|||
"limit": 5,
|
||||
"size": pagination_size
|
||||
},
|
||||
"filtering": {
|
||||
"enabled": true,
|
||||
"position": "left",
|
||||
"placeholder": lang.filter_table
|
||||
},
|
||||
"sorting": {
|
||||
"enabled": true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function draw_filter_table() {
|
||||
ft_filter_table = FooTable.init('#filter_table', {
|
||||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}},
|
||||
{"name":"active","style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
|
||||
{"name":"filter_type","style":{"maxWidth":"80px","width":"80px"},"title":"Type"},
|
||||
{"sorted": true,"name":"username","title":lang.owner,"style":{"maxWidth":"550px","width":"350px"}},
|
||||
{"name":"script_desc","title":lang.description,"breakpoints":"xs"},
|
||||
{"name":"script_data","title":"Script","breakpoints":"all"},
|
||||
{"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/filters/all',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw filter table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.each(data, function (i, item) {
|
||||
if (item.active_int == 1) {
|
||||
item.active = '<span id="active-script" class="label label-success">' + lang.active + '</span>';
|
||||
} else {
|
||||
item.active = '<span id="inactive-script" class="label label-warning">' + lang.inactive + '</span>';
|
||||
}
|
||||
item.script_data = '<pre style="margin:0px">' + escapeHtml(item.script_data) + '</pre>'
|
||||
item.filter_type = '<div class="label label-default">' + item.filter_type.charAt(0).toUpperCase() + item.filter_type.slice(1).toLowerCase() + '</div>'
|
||||
item.action = '<div class="btn-group">' +
|
||||
'<a href="/edit.php?filter=' + 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-filter" data-api-url="delete/filter" 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="filter_item" name="multi_select" value="' + item.id + '" />'
|
||||
});
|
||||
}
|
||||
}),
|
||||
"paging": {
|
||||
"enabled": true,
|
||||
"limit": 5,
|
||||
"size": pagination_size
|
||||
},
|
||||
"filtering": {
|
||||
"enabled": true,
|
||||
"position": "left",
|
||||
"placeholder": lang.filter_table
|
||||
},
|
||||
"sorting": {
|
||||
"enabled": true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
draw_domain_table();
|
||||
draw_mailbox_table();
|
||||
draw_resource_table();
|
||||
draw_alias_table();
|
||||
draw_aliasdomain_table();
|
||||
draw_sync_job_table();
|
||||
});
|
||||
draw_filter_table();
|
||||
|
||||
});
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
!function(e){function t(t,n){var a=e('<div class="numberedtextarea-wrapper"></div>').insertAfter(t);e(t).detach().appendTo(a)}function n(t,n){(t=e(t)).parents(".numberedtextarea-wrapper");var i=parseFloat(t.css("padding-left")),o=parseFloat(t.css("padding-top")),s=(parseFloat(t.css("padding-bottom")),e('<div class="numberedtextarea-line-numbers"></div>').insertAfter(t));t.css({paddingLeft:i+s.width()+"px"}).on("input propertychange change keyup paste",function(){a(t,n)}).on("scroll",function(){r(t,n)}),s.css({paddingLeft:i+"px",paddingTop:o+"px",lineHeight:t.css("line-height"),fontFamily:t.css("font-family"),width:s.width()-i+"px"}),t.trigger("change")}function a(t,n){var a=(t=e(t)).parent().find(".numberedtextarea-line-numbers"),r=t.val().split("\n").length,o=parseFloat(t.css("padding-bottom"));for(a.find(".numberedtextarea-number").remove(),i=1;i<=r;i++){var s=e('<div class="numberedtextarea-number numberedtextarea-number-'+i+'">'+i+"</div>").appendTo(a);i===r&&s.css("margin-bottom",o+"px")}}function r(t,n){(t=e(t)).parent().find(".numberedtextarea-line-numbers").scrollTop(t.scrollTop())}function o(e,t){if(e.focus(),"number"==typeof e.selectionStart){var n=e.value,a=e.selectionStart;e.value=n.slice(0,a)+t+n.slice(e.selectionEnd),e.selectionEnd=e.selectionStart=a+t.length}else if(void 0!==document.selection){var i=document.selection.createRange();i.text=t,i.collapse(!1),i.select()}}function s(t){e(t).keydown(function(e){if(9==e.which)return o(this,"\t"),!1}),e(t).keypress(function(e){if(9==e.which)return!1})}e.fn.numberedtextarea=function(a){var i=e.extend({color:null,borderColor:null,class:null,allowTabChar:!1},a);return this.each(function(){if("textarea"!==this.nodeName.toLowerCase())return console.log("This is not a <textarea>, so no way Jose..."),!1;t(this,i),n(this,i),i.allowTabChar&&e(this).allowTabChar()}),this},e.fn.allowTabChar=function(){return this.jquery&&this.each(function(){if(1==this.nodeType){var e=this.nodeName.toLowerCase();("textarea"==e||"input"==e&&"text"==this.type)&&s(this)}}),this}}(jQuery);
|
|
@ -1,13 +1,19 @@
|
|||
$(document).ready(function() {
|
||||
|
||||
// Log modal
|
||||
$('#logModal').on('show.bs.modal', function(e) {
|
||||
var logText = $(e.relatedTarget).data('log-text');
|
||||
$(e.currentTarget).find('#logText').html('<pre style="background:none;font-size:11px;line-height:1.1;border:0px">' + logText + '</pre>');
|
||||
$('#syncjobLogModal').on('show.bs.modal', function(e) {
|
||||
var syncjob_id = $(e.relatedTarget).data('syncjob-id');
|
||||
$.ajax({
|
||||
url: '/inc/ajax/syncjob_logs.php',
|
||||
data: { id: syncjob_id },
|
||||
dataType: 'text',
|
||||
success: function(data){
|
||||
$(e.currentTarget).find('#logText').text(data);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
$(e.currentTarget).find('#logText').text(xhr.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
jQuery(function($){
|
||||
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
|
||||
var entityMap = {
|
||||
|
@ -83,27 +89,32 @@ jQuery(function($){
|
|||
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}},
|
||||
{"name":"server_w_port","title":"Server"},
|
||||
{"name":"enc1","title":lang.encryption},
|
||||
{"name":"enc1","title":lang.encryption,"breakpoints":"xs sm"},
|
||||
{"name":"user1","title":lang.username},
|
||||
{"name":"exclude","title":lang.excludes},
|
||||
{"name":"exclude","title":lang.excludes,"breakpoints":"xs sm"},
|
||||
{"name":"mins_interval","title":lang.interval + " (min)"},
|
||||
{"name":"last_run","title":lang.last_run},
|
||||
{"name":"last_run","title":lang.last_run,"breakpoints":"xs sm"},
|
||||
{"name":"log","title":"Log"},
|
||||
{"name":"active","filterable": false,"style":{"maxWidth":"50px","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":"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/syncjobs/' + mailcow_cc_username,
|
||||
url: '/api/v1/get/syncjobs/' + mailcow_cc_username + '/no_log',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw sync job table');
|
||||
},
|
||||
success: function (data) {
|
||||
$.each(data, function (i, item) {
|
||||
item.log = '<a href="#logModal" data-toggle="modal" data-log-text="' + escapeHtml(item.returned_text) + '">Open logs</a>'
|
||||
item.exclude = '<code>' + item.exclude + '</code>'
|
||||
item.log = '<a href="#syncjobLogModal" data-toggle="modal" data-syncjob-id="' + encodeURI(item.id) + '">Open logs</a>'
|
||||
if (!item.exclude > 0) {
|
||||
item.exclude = '-';
|
||||
} else {
|
||||
item.exclude = '<code>' + item.exclude + '</code>';
|
||||
}
|
||||
item.server_w_port = item.user1 + '@' + item.host1 + ':' + item.port1;
|
||||
if (acl_data.syncjobs === 1) {
|
||||
item.action = '<div class="btn-group">' +
|
||||
|
@ -116,6 +127,14 @@ jQuery(function($){
|
|||
item.action = '<span>-</span>';
|
||||
item.chkbox = '<input type="checkbox" disabled />';
|
||||
}
|
||||
if (item.is_running == 1) {
|
||||
item.is_running = '<span id="active-script" class="label label-success">' + lang.running + '</span>';
|
||||
} else {
|
||||
item.is_running = '<span id="inactive-script" class="label label-warning">' + lang.waiting + '</span>';
|
||||
}
|
||||
if (!item.last_run > 0) {
|
||||
item.last_run = lang.waiting;
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
@ -213,4 +232,27 @@ jQuery(function($){
|
|||
draw_tla_table();
|
||||
draw_wl_policy_mailbox_table();
|
||||
draw_bl_policy_mailbox_table();
|
||||
|
||||
// Sieve data modal
|
||||
$('#userFilterModal').on('show.bs.modal', function(e) {
|
||||
$('#user_sieve_filter').text(lang.loading);
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: '/api/v1/get/active-user-sieve/' + mailcow_cc_username,
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot get active sieve script');
|
||||
},
|
||||
complete: function (data) {
|
||||
if (data.responseText == '{}') {
|
||||
$('#user_sieve_filter').text(lang.no_active_filter);
|
||||
} else {
|
||||
$('#user_sieve_filter').text(JSON.parse(data.responseText));
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
$('#userFilterModal').on('hidden.bs.modal', function () {
|
||||
$('#user_sieve_filter').text(lang.loading);
|
||||
});
|
||||
});
|
|
@ -13,7 +13,7 @@ delete/alias => POST data:
|
|||
|
||||
*/
|
||||
header('Content-Type: application/json');
|
||||
require_once 'inc/prerequisites.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||
error_reporting(0);
|
||||
if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) {
|
||||
if (isset($_GET['query'])) {
|
||||
|
@ -225,10 +225,10 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
));
|
||||
}
|
||||
break;
|
||||
case "syncjob":
|
||||
case "filter":
|
||||
if (isset($_POST['attr'])) {
|
||||
$attr = (array)json_decode($_POST['attr'], true);
|
||||
if (mailbox('add', 'syncjob', $attr) === false) {
|
||||
if (mailbox('add', 'filter', $attr) === false) {
|
||||
if (isset($_SESSION['return'])) {
|
||||
echo json_encode($_SESSION['return']);
|
||||
}
|
||||
|
@ -489,6 +489,39 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
));
|
||||
}
|
||||
break;
|
||||
case "syncjob":
|
||||
if (isset($_POST['attr'])) {
|
||||
$attr = (array)json_decode($_POST['attr'], true);
|
||||
if (mailbox('add', 'syncjob', $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;
|
||||
case "get":
|
||||
|
@ -725,12 +758,13 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
case "logs":
|
||||
switch ($object) {
|
||||
case "dovecot":
|
||||
if (isset($extra) && !empty($extra)) {
|
||||
$extra = intval($extra);
|
||||
// 0 is first record, so empty is fine
|
||||
if (isset($extra)) {
|
||||
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||
$logs = get_logs('dovecot-mailcow', $extra);
|
||||
}
|
||||
else {
|
||||
$logs = get_logs('dovecot-mailcow', -1);
|
||||
$logs = get_logs('dovecot-mailcow');
|
||||
}
|
||||
if (isset($logs) && !empty($logs)) {
|
||||
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
|
@ -740,12 +774,13 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
}
|
||||
break;
|
||||
case "fail2ban":
|
||||
if (isset($extra) && !empty($extra)) {
|
||||
$extra = intval($extra);
|
||||
// 0 is first record, so empty is fine
|
||||
if (isset($extra)) {
|
||||
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||
$logs = get_logs('fail2ban-mailcow', $extra);
|
||||
}
|
||||
else {
|
||||
$logs = get_logs('fail2ban-mailcow', -1);
|
||||
$logs = get_logs('fail2ban-mailcow');
|
||||
}
|
||||
if (isset($logs) && !empty($logs)) {
|
||||
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
|
@ -755,12 +790,13 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
}
|
||||
break;
|
||||
case "postfix":
|
||||
if (isset($extra) && !empty($extra)) {
|
||||
$extra = intval($extra);
|
||||
// 0 is first record, so empty is fine
|
||||
if (isset($extra)) {
|
||||
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||
$logs = get_logs('postfix-mailcow', $extra);
|
||||
}
|
||||
else {
|
||||
$logs = get_logs('postfix-mailcow', -1);
|
||||
$logs = get_logs('postfix-mailcow');
|
||||
}
|
||||
if (isset($logs) && !empty($logs)) {
|
||||
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
|
@ -770,12 +806,13 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
}
|
||||
break;
|
||||
case "autodiscover":
|
||||
if (isset($extra) && !empty($extra)) {
|
||||
$extra = intval($extra);
|
||||
// 0 is first record, so empty is fine
|
||||
if (isset($extra)) {
|
||||
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||
$logs = get_logs('autodiscover-mailcow', $extra);
|
||||
}
|
||||
else {
|
||||
$logs = get_logs('autodiscover-mailcow', -1);
|
||||
$logs = get_logs('autodiscover-mailcow');
|
||||
}
|
||||
if (isset($logs) && !empty($logs)) {
|
||||
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
|
@ -785,12 +822,13 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
}
|
||||
break;
|
||||
case "sogo":
|
||||
if (isset($extra) && !empty($extra)) {
|
||||
$extra = intval($extra);
|
||||
// 0 is first record, so empty is fine
|
||||
if (isset($extra)) {
|
||||
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||
$logs = get_logs('sogo-mailcow', $extra);
|
||||
}
|
||||
else {
|
||||
$logs = get_logs('sogo-mailcow', -1);
|
||||
$logs = get_logs('sogo-mailcow');
|
||||
}
|
||||
if (isset($logs) && !empty($logs)) {
|
||||
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
|
@ -800,7 +838,14 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
}
|
||||
break;
|
||||
case "rspamd-history":
|
||||
$logs = get_logs('rspamd-history');
|
||||
// 0 is first record, so empty is fine
|
||||
if (isset($extra)) {
|
||||
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||
$logs = get_logs('rspamd-history', $extra);
|
||||
}
|
||||
else {
|
||||
$logs = get_logs('rspamd-history');
|
||||
}
|
||||
if (isset($logs) && !empty($logs)) {
|
||||
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
@ -863,7 +908,13 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
$syncjobs = mailbox('get', 'syncjobs', $mailbox);
|
||||
if (!empty($syncjobs)) {
|
||||
foreach ($syncjobs as $syncjob) {
|
||||
if ($details = mailbox('get', 'syncjob_details', $syncjob)) {
|
||||
if (isset($extra)) {
|
||||
$details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra));
|
||||
}
|
||||
else {
|
||||
$details = mailbox('get', 'syncjob_details', $syncjob);
|
||||
}
|
||||
if ($details) {
|
||||
$data[] = $details;
|
||||
}
|
||||
else {
|
||||
|
@ -890,7 +941,83 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
$syncjobs = mailbox('get', 'syncjobs', $object);
|
||||
if (!empty($syncjobs)) {
|
||||
foreach ($syncjobs as $syncjob) {
|
||||
if ($details = mailbox('get', 'syncjob_details', $syncjob)) {
|
||||
if (isset($extra)) {
|
||||
$details = mailbox('get', 'syncjob_details', $syncjob, explode(',', $extra));
|
||||
}
|
||||
else {
|
||||
$details = mailbox('get', 'syncjob_details', $syncjob);
|
||||
}
|
||||
if ($details) {
|
||||
$data[] = $details;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($data) || empty($data)) {
|
||||
echo '{}';
|
||||
}
|
||||
else {
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "active-user-sieve":
|
||||
if (isset($object)) {
|
||||
$sieve_filter = mailbox('get', 'active_user_sieve', $object);
|
||||
if (!empty($sieve_filter)) {
|
||||
$data[] = $sieve_filter;
|
||||
}
|
||||
}
|
||||
if (!isset($data) || empty($data)) {
|
||||
echo '{}';
|
||||
}
|
||||
else {
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
break;
|
||||
case "filters":
|
||||
switch ($object) {
|
||||
case "all":
|
||||
$domains = mailbox('get', 'domains');
|
||||
if (!empty($domains)) {
|
||||
foreach ($domains as $domain) {
|
||||
$mailboxes = mailbox('get', 'mailboxes', $domain);
|
||||
if (!empty($mailboxes)) {
|
||||
foreach ($mailboxes as $mailbox) {
|
||||
$filters = mailbox('get', 'filters', $mailbox);
|
||||
if (!empty($filters)) {
|
||||
foreach ($filters as $filter) {
|
||||
if ($details = mailbox('get', 'filter_details', $filter)) {
|
||||
$data[] = $details;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($data) || empty($data)) {
|
||||
echo '{}';
|
||||
}
|
||||
else {
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
echo '{}';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$filters = mailbox('get', 'filters', $object);
|
||||
if (!empty($filters)) {
|
||||
foreach ($filters as $filter) {
|
||||
if ($details = mailbox('get', 'filter_details', $filter)) {
|
||||
$data[] = $details;
|
||||
}
|
||||
else {
|
||||
|
@ -1296,6 +1423,47 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
));
|
||||
}
|
||||
break;
|
||||
case "filter":
|
||||
if (isset($_POST['items'])) {
|
||||
$items = (array)json_decode($_POST['items'], true);
|
||||
if (is_array($items)) {
|
||||
if (mailbox('delete', 'filter', 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":
|
||||
if (isset($_POST['items'])) {
|
||||
$items = (array)json_decode($_POST['items'], true);
|
||||
|
@ -2102,6 +2270,50 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
));
|
||||
}
|
||||
break;
|
||||
case "filter":
|
||||
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 (mailbox('edit', 'filter', $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 "resource":
|
||||
if (isset($_POST['items']) && isset($_POST['attr'])) {
|
||||
$items = (array)json_decode($_POST['items'], true);
|
||||
|
|
|
@ -99,6 +99,9 @@ $lang['danger']['domain_not_empty'] = 'Kann nur leere Domains entfernen';
|
|||
$lang['warning']['spam_alias_temp_error'] = 'Kann zur Zeit keinen Spam-Alias erstellen, bitte versuchen Sie es später noch einmal.';
|
||||
$lang['danger']['spam_alias_max_exceeded'] = 'Maximale Anzahl an Spam-Alias-Adressen erreicht';
|
||||
$lang['danger']['validity_missing'] = 'Bitte geben Sie eine Gültigkeitsdauer an';
|
||||
$lang['user']['loading'] = "Lade...";
|
||||
$lang['user']['active_sieve'] = "Aktiver Filter";
|
||||
$lang['user']['no_active_filter'] = "Kein aktiver Filter vorhanden";
|
||||
$lang['user']['on'] = 'Ein';
|
||||
$lang['user']['off'] = 'Aus';
|
||||
$lang['user']['messages'] = "Nachrichten";
|
||||
|
@ -155,6 +158,9 @@ $lang['user']['spamfilter_red'] = 'Rot: Die Nachricht ist eindeutig Spam und wir
|
|||
$lang['user']['spamfilter_default_score'] = 'Standardwert:';
|
||||
$lang['user']['spamfilter_hint'] = 'Der erste Wert beschreibt den "low spam score", der zweite Wert den "high spam score".';
|
||||
$lang['user']['spamfilter_table_domain_policy'] = "n.v. (Domainrichtlinie)";
|
||||
$lang['user']['waiting'] = "Warte auf Ausführung";
|
||||
$lang['user']['status'] = "Status";
|
||||
$lang['user']['running'] = "Wird ausgeführt";
|
||||
|
||||
$lang['user']['tls_policy_warning'] = '<strong>Vorsicht:</strong> Entscheiden Sie sich unverschlüsselte Verbindungen abzulehnen, kann dies dazu führen, dass Kontakte Sie nicht mehr erreichen.<br>Nachrichten, die die Richtlinie nicht erfüllen, werden durch einen Hard-Fail im Mailsystem abgewiesen.<br>Diese Einstellung ist aktiv für die primäre Mailbox, für alle Alias-Adressen, die dieser Mailbox <b>direkt zugeordnet</b> sind (lediglich eine einzige Ziel-Adresse) und der Adressen, die sich aus Alias-Domains ergeben. Ausgeschlossen sind temporäre Aliasse ("Spam-Alias-Adressen"), Catch-All Alias-Adressen sowie Alias-Adressen mit mehreren Zielen.';
|
||||
$lang['user']['tls_policy'] = 'Verschlüsselungsrichtlinie';
|
||||
|
@ -215,6 +221,7 @@ $lang['header']['mailcow_settings'] = 'Konfiguration';
|
|||
$lang['header']['administration'] = 'Administration';
|
||||
$lang['header']['mailboxes'] = 'Mailboxen';
|
||||
$lang['header']['user_settings'] = 'Benutzereinstellungen';
|
||||
$lang['header']['diagnostics'] = 'Diagnose';
|
||||
$lang['header']['login'] = 'Anmeldung';
|
||||
$lang['header']['logged_in_as_logout'] = 'Eingeloggt als <b>%s</b> (abmelden)';
|
||||
$lang['header']['logged_in_as_logout_dual'] = 'Eingeloggt als <b>%s <span class="text-info">[%s]</span></b>';
|
||||
|
@ -265,6 +272,10 @@ $lang['mailbox']['deactivate'] = 'Deaktivieren';
|
|||
$lang['mailbox']['owner'] = 'Besitzer';
|
||||
$lang['mailbox']['mins_interval'] = 'Intervall (min)';
|
||||
$lang['mailbox']['last_run'] = 'Letzte Ausführung';
|
||||
$lang['mailbox']['last_run_reset'] = 'Als nächstes ausführen';
|
||||
$lang['mailbox']['sieve_info'] = 'Es können mehrere Filter pro Benutzer existieren, aber nur ein Filter eines Typs (Pre-/Postfilter) kann gleichzeitig aktiv sein.<br>
|
||||
Die Ausführung erfolgt in nachstehender Reihenfolge. Ein fehlgeschlagenes Script sowie der Befehl "keep;" stoppen die weitere Verarbeitung <b>nicht</b>.<br>
|
||||
Prefilter → User scripts → Postfilter → <a href="https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/sieve_after" target="_blank">global sieve postfilter</a>';
|
||||
|
||||
$lang['info']['no_action'] = 'Keine Aktion anwendbar';
|
||||
$lang['delete']['title'] = 'Objekt entfernen';
|
||||
|
@ -287,7 +298,7 @@ $lang['edit']['syncjob'] = 'Sync-Job bearbeiten';
|
|||
$lang['edit']['save'] = 'Änderungen speichern';
|
||||
$lang['edit']['username'] = 'Benutzername';
|
||||
$lang['edit']['hostname'] = 'Servername';
|
||||
$lang['edit']['encryption'] = 'Verschlüsselungsmethode';
|
||||
$lang['edit']['encryption'] = 'Verschlüsselung';
|
||||
$lang['edit']['maxage'] = 'Maximales Alter in Tagen einer Nachricht, die kopiert werden soll</br ><small>(0 = alle Nachrichten kopieren)</small>';
|
||||
$lang['edit']['subfolder2'] = 'Ziel-Ordner<br><small>(leer = kein Unterordner)</small>';
|
||||
$lang['edit']['mins_interval'] = 'Intervall (min)';
|
||||
|
@ -339,7 +350,7 @@ $lang['add']['syncjob_hint'] = 'Passwörter werden unverschlüsselt abgelegt!';
|
|||
$lang['add']['hostname'] = 'Servername';
|
||||
$lang['add']['port'] = 'Port';
|
||||
$lang['add']['username'] = 'Benutzername';
|
||||
$lang['add']['enc_method'] = 'Verschlüsselungsmethode';
|
||||
$lang['add']['enc_method'] = 'Verschlüsselung';
|
||||
$lang['add']['maxage'] = 'Maximales Alter von Nachrichten, welche vom Remote abgefragt werden (0 = Alter ignorieren)';
|
||||
$lang['add']['subfolder2'] = 'Synchronisation in Unterordner am Ziel';
|
||||
$lang['add']['mins_interval'] = 'Abrufintervall (Minuten)';
|
||||
|
@ -388,6 +399,21 @@ $lang['add']['password_repeat'] = 'Passwort (Wiederholung)';
|
|||
$lang['add']['previous'] = 'Vorherige Seite';
|
||||
$lang['add']['restart_sogo_hint'] = 'Der SOGo Container muss nach dem Hinzufügen einer neuen Domain neugestartet werden!';
|
||||
$lang['add']['goto_null'] = 'Nachrichten sofort verwerfen';
|
||||
$lang['add']['validation_success'] = 'Erfolgreich validiert';
|
||||
$lang['add']['activate_filter_warn'] = 'Alle anderen Filter diesen Typs werden deaktiviert, falls dieses Script aktiv markiert wird.';
|
||||
$lang['add']['validate'] = 'Validieren';
|
||||
$lang['mailbox']['add_filter'] = 'Filter erstellen';
|
||||
$lang['add']['sieve_desc'] = 'Kurze Beschreibung';
|
||||
$lang['edit']['sieve_desc'] = 'Kurze Beschreibung';
|
||||
$lang['add']['sieve_type'] = 'Filtertyp';
|
||||
$lang['edit']['sieve_type'] = 'Filtertyp';
|
||||
$lang['mailbox']['set_prefilter'] = 'Als Prefilter markieren';
|
||||
$lang['mailbox']['set_postfilter'] = 'Als Postfilter markieren';
|
||||
$lang['mailbox']['filters'] = 'Filter';
|
||||
$lang['mailbox']['sync_jobs'] = 'Synchronisationen';
|
||||
$lang['mailbox']['inactive'] = 'Inaktiv';
|
||||
$lang['edit']['validate_save'] = 'Validieren und speichern';
|
||||
|
||||
|
||||
$lang['login']['title'] = 'Anmeldung';
|
||||
$lang['login']['administration'] = 'Administration';
|
||||
|
@ -425,6 +451,8 @@ $lang['tfa']['scan_qr_code'] = "Bitte scannen Sie jetzt den angezeigten QR-Code:
|
|||
$lang['tfa']['enter_qr_code'] = "Falls Sie den angezeigten QR-Code nicht scannen können, verwenden Sie bitte nachstehenden Sicherheitsschlüssel";
|
||||
$lang['tfa']['confirm_totp_token'] = "Bitte bestätigen Sie die Änderung durch Eingabe eines generierten Tokens";
|
||||
|
||||
$lang['admin']['no_new_rows'] = 'Keine weiteren Zeilen vorhanden';
|
||||
$lang['admin']['additional_rows'] = ' zusätzliche Zeilen geladen'; // parses to 'n additional rows were added'
|
||||
$lang['admin']['private_key'] = 'Private Key';
|
||||
$lang['admin']['import'] = 'Importieren';
|
||||
$lang['admin']['import_private_key'] = 'Private Key importieren';
|
||||
|
@ -510,6 +538,12 @@ $lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entf
|
|||
$lang['success']['forwarding_host_added'] = "Weiterleitungs-Host %s wurde hinzugefügt";
|
||||
$lang['success']['relayhost_removed'] = "Relayhost %s wurde entfernt";
|
||||
$lang['success']['relayhost_added'] = "Relayhost %s wurde hinzugefügt";
|
||||
$lang['diagnostics']['dns_records'] = 'DNS-Einträge';
|
||||
$lang['diagnostics']['dns_records_24hours'] = 'Bitte beachten Sie, dass es bis zu 24 Stunden dauern kann, bis Änderungen an Ihren DNS-Einträgen als aktueller Status auf dieser Seite dargestellt werden. Diese Seite ist nur als Hilfsmittel gedacht, um die korrekten Werte für DNS-Einträge zu anzuzeigen und zu überprüfen, ob die Daten im DNS hinterlegt sind.';
|
||||
$lang['diagnostics']['dns_records_name'] = 'Name';
|
||||
$lang['diagnostics']['dns_records_type'] = 'Typ';
|
||||
$lang['diagnostics']['dns_records_data'] = 'Korrekte Daten';
|
||||
$lang['diagnostics']['dns_records_status'] = 'Aktueller Status';
|
||||
$lang['admin']['relay_from'] = "Absenderadresse";
|
||||
$lang['admin']['relay_run'] = "Test durchführen";
|
||||
$lang['admin']['customize'] = "Anpassung";
|
||||
|
@ -523,3 +557,6 @@ $lang['admin']['remove_row'] = "Zeile entfernen";
|
|||
$lang['admin']['add_row'] = "Zeile hinzufügen";
|
||||
$lang['admin']['reset_default'] = "Auf Standard zurücksetzen";
|
||||
$lang['admin']['merged_vars_hint'] = 'Ausgegraute Zeilen wurden aus der Datei <code>vars.inc.(local.)php</code> gelesen und können nicht mittels UI verändert werden.';
|
||||
$lang['mailbox']['waiting'] = "Wartend";
|
||||
$lang['mailbox']['status'] = "Status";
|
||||
$lang['mailbox']['running'] = "In Ausführung";
|
|
@ -99,6 +99,9 @@ $lang['danger']['domain_not_empty'] = "Cannot remove non-empty domain";
|
|||
$lang['warning']['spam_alias_temp_error'] = "Temporary error: Cannot add spam alias, please try again later.";
|
||||
$lang['danger']['spam_alias_max_exceeded'] = "Max. allowed spam alias addresses exceeded";
|
||||
$lang['danger']['validity_missing'] = 'Please assign a period of validity';
|
||||
$lang['user']['loading'] = "Loading...";
|
||||
$lang['user']['active_sieve'] = "Active filter";
|
||||
$lang['user']['no_active_filter'] = "No active filter available";
|
||||
$lang['user']['on'] = "On";
|
||||
$lang['user']['off'] = "Off";
|
||||
$lang['user']['messages'] = "messages"; // "123 messages"
|
||||
|
@ -154,6 +157,9 @@ $lang['user']['spamfilter_red'] = 'Red: This message is spam and will be rejecte
|
|||
$lang['user']['spamfilter_default_score'] = 'Default values:';
|
||||
$lang['user']['spamfilter_hint'] = 'The first value describes the "low spam score", the second represents the "high spam score".';
|
||||
$lang['user']['spamfilter_table_domain_policy'] = "n/a (domain policy)";
|
||||
$lang['user']['waiting'] = "Waiting";
|
||||
$lang['user']['status'] = "Status";
|
||||
$lang['user']['running'] = "Running";
|
||||
|
||||
$lang['user']['tls_policy_warning'] = '<strong>Warning:</strong> If you decide to enforce encrypted mail transfer, you may lose emails.<br>Messages to not satisfy the policy will be bounced with a hard fail by the mail system.<br>This option applies to your primary email address (login name), all addresses derived from alias domains as well as alias addresses <b>with only this single mailbox</b> as target.';
|
||||
$lang['user']['tls_policy'] = 'Encryption policy';
|
||||
|
@ -214,6 +220,7 @@ $lang['header']['mailcow_settings'] = 'Configuration';
|
|||
$lang['header']['administration'] = 'Administration';
|
||||
$lang['header']['mailboxes'] = 'Mailboxes';
|
||||
$lang['header']['user_settings'] = 'User settings';
|
||||
$lang['header']['diagnostics'] = 'Diagnostics';
|
||||
$lang['header']['login'] = 'Login';
|
||||
$lang['header']['logged_in_as_logout'] = 'Logged in as <b>%s</b> (logout)';
|
||||
$lang['header']['logged_in_as_logout_dual'] = 'Logged in as <b>%s <span class="text-info">[%s]</span></b>';
|
||||
|
@ -265,7 +272,10 @@ $lang['mailbox']['deactivate'] = 'Deactivate';
|
|||
$lang['mailbox']['owner'] = 'Owner';
|
||||
$lang['mailbox']['mins_interval'] = 'Interval (min)';
|
||||
$lang['mailbox']['last_run'] = 'Last run';
|
||||
|
||||
$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>
|
||||
Each filter will be processed in the described order. Neither a failed script nor an issued "keep;" will stop processing of further scripts.<br>
|
||||
Prefilter → User scripts → Postfilter → <a href="https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/sieve_after" target="_blank">global sieve postfilter</a>';
|
||||
$lang['info']['no_action'] = 'No action applicable';
|
||||
|
||||
$lang['delete']['title'] = 'Remove object';
|
||||
|
@ -389,6 +399,21 @@ $lang['add']['password_repeat'] = 'Confirmation password (repeat)';
|
|||
$lang['add']['previous'] = 'Previous page';
|
||||
$lang['add']['restart_sogo_hint'] = 'You will need to restart the SOGo service container after adding a new domain!';
|
||||
$lang['add']['goto_null'] = 'Silently discard mail';
|
||||
$lang['add']['validation_success'] = 'Validated successfully';
|
||||
$lang['add']['activate_filter_warn'] = 'All other filters will be deactivated, when active is checked.';
|
||||
$lang['add']['validate'] = 'Validate';
|
||||
$lang['mailbox']['add_filter'] = 'Add filter';
|
||||
$lang['add']['sieve_desc'] = 'Short description';
|
||||
$lang['edit']['sieve_desc'] = 'Short description';
|
||||
$lang['add']['sieve_type'] = 'Filter type';
|
||||
$lang['edit']['sieve_type'] = 'Filter type';
|
||||
$lang['mailbox']['set_prefilter'] = 'Mark as prefilter';
|
||||
$lang['mailbox']['set_postfilter'] = 'Mark as postfilter';
|
||||
$lang['mailbox']['filters'] = 'Filters';
|
||||
$lang['mailbox']['sync_jobs'] = 'Sync jobs';
|
||||
$lang['mailbox']['inactive'] = 'Inactive';
|
||||
$lang['edit']['validate_save'] = 'Validate and save';
|
||||
|
||||
|
||||
$lang['login']['title'] = 'Login';
|
||||
$lang['login']['administration'] = 'Administration';
|
||||
|
@ -425,6 +450,8 @@ $lang['tfa']['scan_qr_code'] = "Please scan the following code with your authent
|
|||
$lang['tfa']['enter_qr_code'] = "Your TOTP code if your device cannot scan QR codes";
|
||||
$lang['tfa']['confirm_totp_token'] = "Please confirm your changes by entering the generated token";
|
||||
|
||||
$lang['admin']['no_new_rows'] = 'No further rows available';
|
||||
$lang['admin']['additional_rows'] = ' additional rows were added'; // parses to 'n additional rows were added'
|
||||
$lang['admin']['private_key'] = 'Private key';
|
||||
$lang['admin']['import'] = 'Import';
|
||||
$lang['admin']['import_private_key'] = 'Import private key';
|
||||
|
@ -516,6 +543,12 @@ $lang['success']['forwarding_host_removed'] = "Forwarding host %s has been remov
|
|||
$lang['success']['forwarding_host_added'] = "Forwarding host %s has been added";
|
||||
$lang['success']['relayhost_removed'] = "Relayhost %s has been removed";
|
||||
$lang['success']['relayhost_added'] = "Relayhost %s has been added";
|
||||
$lang['diagnostics']['dns_records'] = 'DNS Records';
|
||||
$lang['diagnostics']['dns_records_24hours'] = 'Please note that changes made to DNS may take up to 24 hours to correctly have their current state reflected on this page. It is intended as a way for you to easily see how to configure your DNS records and to check whether all your records are correctly stored in DNS.';
|
||||
$lang['diagnostics']['dns_records_name'] = 'Name';
|
||||
$lang['diagnostics']['dns_records_type'] = 'Type';
|
||||
$lang['diagnostics']['dns_records_data'] = 'Correct Data';
|
||||
$lang['diagnostics']['dns_records_status'] = 'Current State';
|
||||
$lang['admin']['relay_from'] = '"From:" address';
|
||||
$lang['admin']['relay_run'] = "Run test";
|
||||
|
||||
|
@ -530,6 +563,9 @@ $lang['admin']['remove_row'] = "Remove row";
|
|||
$lang['admin']['add_row'] = "Add row";
|
||||
$lang['admin']['reset_default'] = "Reset to default";
|
||||
$lang['admin']['merged_vars_hint'] = 'Greyed out rows were merged from <code>vars.inc.(local.)php</code> and cannot be modified.';
|
||||
$lang['mailbox']['waiting'] = "Waiting";
|
||||
$lang['mailbox']['status'] = "Status";
|
||||
$lang['mailbox']['running'] = "Running";
|
||||
|
||||
$lang['edit']['tls_policy'] = "Change TLS policy";
|
||||
$lang['edit']['spam_score'] = "Set a custom spam score";
|
||||
|
|
|
@ -19,7 +19,8 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
<li role="presentation"><a href="#tab-domain-aliases" aria-controls="tab-domain-aliases" role="tab" data-toggle="tab"><?=$lang['mailbox']['domain_aliases'];?></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li role="presentation"><a href="#tab-syncjobs" aria-controls="tab-resources" role="tab" data-toggle="tab">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>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
|
@ -155,7 +156,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
<div role="tabpanel" class="tab-pane" id="tab-syncjobs">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Sync jobs</h3>
|
||||
<h3 class="panel-title"><?=$lang['mailbox']['sync_jobs'];?></h3>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="sync_job_table"></table>
|
||||
|
@ -165,6 +166,8 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="syncjob" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
|
||||
<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="syncjob" data-api-url='edit/syncjob' data-api-attr='{"last_run":""}' href="#"><?=$lang['mailbox']['last_run_reset'];?></a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a id="edit_selected" data-id="syncjob" data-api-url='edit/syncjob' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
|
||||
<li><a id="edit_selected" data-id="syncjob" data-api-url='edit/syncjob' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
|
@ -173,6 +176,35 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
<a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addSyncJobModalAdmin"><span class="glyphicon glyphicon-plus"></span> <?=$lang['user']['create_syncjob'];?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-filters">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><?=$lang['mailbox']['filters'];?></h3>
|
||||
</div>
|
||||
<p style="margin:10px" class="help-block"><?=$lang['mailbox']['sieve_info'];?></p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="filter_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="filter_item" 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="filter_item" data-api-url='edit/filter' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
|
||||
<li><a id="edit_selected" data-id="filter_item" data-api-url='edit/filter' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a id="edit_selected" data-id="filter_item" data-api-url='edit/filter' data-api-attr='{"filter_type":"prefilter"}' href="#"><?=$lang['mailbox']['set_prefilter'];?></a></li>
|
||||
<li><a id="edit_selected" data-id="filter_item" data-api-url='edit/filter' data-api-attr='{"filter_type":"postfilter"}' href="#"><?=$lang['mailbox']['set_postfilter'];?></a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a id="delete_selected" data-text="<?=$lang['user']['eas_reset'];?>?" data-id="filter_item" data-api-url='delete/filter' href="#"><?=$lang['mailbox']['remove'];?></a></li>
|
||||
</ul>
|
||||
<a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addFilterModalAdmin"><span class="glyphicon glyphicon-plus"></span> <?=$lang['mailbox']['add_filter'];?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /tab-content -->
|
||||
|
|
|
@ -44,6 +44,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="quota" min="1" max="" id="addInputQuota" disabled value="<?=$lang['add']['select_domain'];?>" required>
|
||||
<small class="help-block">min. 1</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -314,7 +315,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
<h3 class="modal-title"><?=$lang['add']['syncjob'];?></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><?=$lang['add']['syncjob_hint'];?></p>
|
||||
<p class="help-block"><?=$lang['add']['syncjob_hint'];?></p>
|
||||
<form class="form-horizontal" role="form" data-id="add_syncjob">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label>
|
||||
|
@ -344,6 +345,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
<label class="control-label col-sm-2" for="port1"><?=$lang['add']['port'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="143" required>
|
||||
<small class="help-block">1-65535</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -372,6 +374,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
<label class="control-label col-sm-2" for="mins_interval"><?=$lang['add']['mins_interval'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="mins_interval" min="10" max="3600" value="20" required>
|
||||
<small class="help-block">10-3600</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -384,6 +387,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="maxage" id="maxage" min="0" max="32000" value="0">
|
||||
<small class="help-block">0-32000</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -437,12 +441,81 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
</div>
|
||||
</div>
|
||||
</div><!-- add sync job modal -->
|
||||
<!-- log modal -->
|
||||
<div class="modal fade" id="logModal" tabindex="-1" role="dialog" aria-labelledby="logTextLabel">
|
||||
<div class="modal-dialog" style="width:90%" role="document">
|
||||
<!-- add add_filter modal -->
|
||||
<div class="modal fade" id="addFilterModalAdmin" 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">Filter</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span id="logText"></span>
|
||||
<form class="form-horizontal" role="form" data-id="add_filter">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select id="addSelectUsername" name="username" id="username" required>
|
||||
<?php
|
||||
$domains = mailbox('get', 'domains');
|
||||
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="filter_type"><?=$lang['add']['sieve_type'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select id="addFilterType" name="filter_type" id="filter_type" required>
|
||||
<option value="prefilter">Prefilter</option>
|
||||
<option value="postfilter">Postfilter</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="script_desc"><?=$lang['add']['sieve_desc'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="script_desc" id="script_desc" required maxlength="255">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="script_data">Script:</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control" rows="20" id="script_data" name="script_data" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<p class="help-block"><?=$lang['add']['activate_filter_warn'];?></p>
|
||||
<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" id="add_filter_btns">
|
||||
<button class="btn btn-default" id="validate_sieve" href="#"><?=$lang['add']['validate'];?></button>
|
||||
<button class="btn btn-success" id="add_item" data-id="add_filter" data-api-url='add/filter' data-api-attr='{}' href="#" disabled><?=$lang['admin']['add'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- add add_filter modal -->
|
||||
<!-- log modal -->
|
||||
<div class="modal fade" id="syncjobLogModal" tabindex="-1" role="dialog" aria-labelledby="syncjobLogModalLabel">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h4 class="modal-title">Log</h4></div>
|
||||
<div class="modal-body">
|
||||
<textarea class="form-control" rows="20" id="logText" spellcheck="false"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,6 +25,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
<label class="control-label col-sm-2" for="port1"><?=$lang['add']['port'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="143" required>
|
||||
<small class="help-block">1-65535</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -53,6 +54,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
<label class="control-label col-sm-2" for="mins_interval"><?=$lang['add']['mins_interval'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="mins_interval" min="10" max="3600" value="20" required>
|
||||
<small class="help-block">10-3600</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -65,6 +67,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="maxage" id="maxage" min="0" max="32000" value="0">
|
||||
<small class="help-block">0-32000</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -112,11 +115,12 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
</div>
|
||||
</div><!-- add sync job modal -->
|
||||
<!-- log modal -->
|
||||
<div class="modal fade" id="logModal" tabindex="-1" role="dialog" aria-labelledby="logTextLabel">
|
||||
<div class="modal-dialog" style="width:90%" role="document">
|
||||
<div class="modal fade" id="syncjobLogModal" tabindex="-1" role="dialog" aria-labelledby="syncjobLogModalLabel">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><h4 class="modal-title">Log</h4></div>
|
||||
<div class="modal-body">
|
||||
<span id="logText"></span>
|
||||
<textarea class="form-control" rows="20" id="logText" spellcheck="false"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -156,4 +160,18 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- pw change modal -->
|
||||
</div><!-- pw change modal -->
|
||||
<!-- sieve filter modal -->
|
||||
<div class="modal fade" id="userFilterModal" tabindex="-1" role="dialog" aria-labelledby="pwChangeModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<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['user']['active_sieve'];?></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<pre id="user_sieve_filter"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- sieve filter modal -->
|
|
@ -94,6 +94,13 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
|
|||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"> <span class="glyphicon glyphicon-filter"></span></div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><a href="#userFilterModal" data-toggle="modal">[Show active user sieve filter]</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<?php // Get user information about aliases
|
||||
$user_get_alias_details = user_get_alias_details($username);
|
||||
?>
|
||||
|
|
|
@ -92,7 +92,7 @@ services:
|
|||
- rspamd
|
||||
|
||||
php-fpm-mailcow:
|
||||
image: mailcow/phpfpm:1.4
|
||||
image: mailcow/phpfpm:1.5
|
||||
build: ./data/Dockerfiles/phpfpm
|
||||
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
||||
depends_on:
|
||||
|
@ -143,8 +143,10 @@ services:
|
|||
- sogo
|
||||
|
||||
dovecot-mailcow:
|
||||
image: mailcow/dovecot:1.9
|
||||
image: mailcow/dovecot:1.12
|
||||
build: ./data/Dockerfiles/dovecot
|
||||
cap_add:
|
||||
- NET_BIND_SERVICE
|
||||
volumes:
|
||||
- ./data/conf/dovecot:/usr/local/etc/dovecot
|
||||
- ./data/assets/ssl:/etc/ssl/mail/:ro
|
||||
|
@ -222,6 +224,7 @@ services:
|
|||
envsubst < /etc/nginx/conf.d/templates/server_name.template > /etc/nginx/conf.d/server_name.active &&
|
||||
nginx -qt &&
|
||||
until ping phpfpm -c1 > /dev/null; do sleep 1; done &&
|
||||
until ping sogo -c1 > /dev/null; do sleep 1; done &&
|
||||
until ping redis -c1 > /dev/null; do sleep 1; done &&
|
||||
exec nginx -g 'daemon off;'"
|
||||
environment:
|
||||
|
@ -249,7 +252,7 @@ services:
|
|||
depends_on:
|
||||
- nginx-mailcow
|
||||
- mysql-mailcow
|
||||
image: mailcow/acme:1.22
|
||||
image: mailcow/acme:1.23
|
||||
build: ./data/Dockerfiles/acme
|
||||
dns:
|
||||
- 172.22.1.254
|
||||
|
@ -272,7 +275,7 @@ services:
|
|||
- acme
|
||||
|
||||
fail2ban-mailcow:
|
||||
image: mailcow/fail2ban:1.7
|
||||
image: mailcow/fail2ban:1.8
|
||||
build: ./data/Dockerfiles/fail2ban
|
||||
stop_grace_period: 30s
|
||||
depends_on:
|
||||
|
@ -293,7 +296,7 @@ services:
|
|||
- /lib/modules:/lib/modules:ro
|
||||
|
||||
watchdog-mailcow:
|
||||
image: mailcow/watchdog:1.9
|
||||
image: mailcow/watchdog:1.10
|
||||
build: ./data/Dockerfiles/watchdog
|
||||
volumes:
|
||||
- vmail-vol-1:/vmail:ro
|
||||
|
@ -312,9 +315,10 @@ services:
|
|||
- watchdog
|
||||
|
||||
dockerapi-mailcow:
|
||||
image: mailcow/dockerapi:1.1
|
||||
stop_grace_period: 3s
|
||||
image: mailcow/dockerapi:1.2
|
||||
restart: always
|
||||
build: ./data/Dockerfiles/dockerapi
|
||||
oom_score_adj: -10
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
networks:
|
||||
|
|
|
@ -13,7 +13,7 @@ if [[ -f mailcow.conf ]]; then
|
|||
fi
|
||||
|
||||
if [ -z "$MAILCOW_HOSTNAME" ]; then
|
||||
read -p "Hostname (FQDN): " -ei "mx.example.org" MAILCOW_HOSTNAME
|
||||
read -p "Hostname (FQDN - example.org is not a valid FQDN): " -ei "mx.example.org" MAILCOW_HOSTNAME
|
||||
fi
|
||||
|
||||
if [[ -a /etc/timezone ]]; then
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
#!/bin/bash
|
||||
|
||||
[[ -z ${1} ]] && { echo "No parameters given"; exit 1; }
|
||||
|
||||
for bin in curl dirmngr; do
|
||||
if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
|
||||
done
|
||||
|
||||
while [ "$1" != '' ]; do
|
||||
case "${1}" in
|
||||
-p|--purge) NC_PURGE=y && shift;;
|
||||
-i|--install) NC_INSTALL=y && shift;;
|
||||
*) echo "Unknown parameter: ${1}" && shift;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ ${NC_PURGE} == "y" ]] && [[ ${NC_INSTALL} == "y" ]] && { echo "Cannot use -p and -i at the same time"; }
|
||||
|
||||
source ./mailcow.conf
|
||||
|
||||
if [[ ${NC_PURGE} == "y" ]]; then
|
||||
|
||||
docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e \
|
||||
"$(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "SELECT GROUP_CONCAT('DROP TABLE ', TABLE_SCHEMA, '.', TABLE_NAME SEPARATOR ';') FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'nc_%' AND TABLE_SCHEMA = '${DBNAME}';" -BN)"
|
||||
docker exec -it $(docker ps -f name=redis-mailcow -q) /bin/sh -c 'redis-cli KEYS "*nextcloud*" | xargs redis-cli DEL'
|
||||
if [ -d ./data/web/nextcloud/config ]; then
|
||||
mv ./data/web/nextcloud/config/ ./data/conf/nextcloud-config-folder-$(date +%s).bak
|
||||
fi
|
||||
[[ -d ./data/web/nextcloud ]] && rm -rf ./data/web/nextcloud
|
||||
|
||||
[[ -f ./data/conf/nginx/site.nextcloud.custom ]] && mv ./data/conf/nginx/site.nextcloud.custom ./data/conf/nginx/site.nextcloud.custom-$(date +%s).bak
|
||||
[[ -f ./data/conf/nginx/nextcloud.conf ]] && mv ./data/conf/nginx/nextcloud.conf ./data/conf/nginx/nextcloud.conf-$(date +%s).bak
|
||||
|
||||
docker-compose restart nginx-mailcow
|
||||
|
||||
elif [[ ${NC_INSTALL} == "y" ]]; then
|
||||
|
||||
NC_TYPE=
|
||||
while [[ ! ${NC_TYPE} =~ ^subfolder$|^subdomain$ ]]; do
|
||||
read -p "Configure as subdomain or subfolder? [subdomain/subfolder] " NC_TYPE
|
||||
done
|
||||
|
||||
|
||||
if [[ ${NC_TYPE} == "subdomain" ]]; then
|
||||
NC_SUBD=
|
||||
while [[ -z ${NC_SUBD} ]]; do
|
||||
read -p "Which subdomain? [format: nextcloud.domain.tld] " NC_SUBD
|
||||
done
|
||||
if ! ping -q -c2 ${NC_SUBD} > /dev/null 2>&1 ; then
|
||||
read -p "Cannot ping subdomain, continue anyway? [y|N] " NC_CONT_FAIL
|
||||
[[ ! ${NC_CONT_FAIL,,} =~ ^(yes|y)$ ]] && { echo "Ok, exiting..."; exit 1; }
|
||||
fi
|
||||
fi
|
||||
|
||||
ADMIN_NC_PASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
|
||||
NEXTCLOUD_VERSION=$(curl -s https://www.servercow.de/nextcloud/latest.php)
|
||||
|
||||
[[ -z ${NEXTCLOUD_VERSION} ]] && { echo "Error, cannot determine nextcloud version, exiting..."; exit 1; }
|
||||
|
||||
curl -L# -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/nextcloud-${NEXTCLOUD_VERSION}.tar.bz2" \
|
||||
&& curl -L# -o nextcloud.tar.bz2.asc "https://download.nextcloud.com/server/releases/nextcloud-${NEXTCLOUD_VERSION}.tar.bz2.asc" \
|
||||
&& export GNUPGHOME="$(mktemp -d)" \
|
||||
&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 28806A878AE423A28372792ED75899B9A724937A \
|
||||
&& gpg --batch --verify nextcloud.tar.bz2.asc nextcloud.tar.bz2 \
|
||||
&& rm -r "$GNUPGHOME" nextcloud.tar.bz2.asc \
|
||||
&& tar -xjf nextcloud.tar.bz2 -C ./data/web/ \
|
||||
&& rm nextcloud.tar.bz2 \
|
||||
&& rm -rf ./data/web/nextcloud/updater \
|
||||
&& mkdir -p ./data/web/nextcloud/data \
|
||||
&& mkdir -p ./data/web/nextcloud/custom_apps \
|
||||
&& chmod +x ./data/web/nextcloud/occ
|
||||
|
||||
docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud/data /web/nextcloud/config /web/nextcloud/apps /web/nextcloud/custom_apps"
|
||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ maintenance:install \
|
||||
--database mysql \
|
||||
--database-host mysql \
|
||||
--database-name ${DBNAME} \
|
||||
--database-user ${DBUSER} \
|
||||
--database-pass ${DBPASS} \
|
||||
--database-table-prefix nc_ \
|
||||
--admin-user admin \
|
||||
--admin-pass ${ADMIN_NC_PASS} \
|
||||
--data-dir /web/nextcloud/data
|
||||
|
||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ config:system:set redis host --value=redis --type=string; \
|
||||
/web/nextcloud/occ config:system:set redis port --value=6379 --type=integer; \
|
||||
/web/nextcloud/occ config:system:set memcache.locking --value='\OC\Memcache\Redis' --type=string; \
|
||||
/web/nextcloud/occ config:system:set memcache.local --value='\OC\Memcache\Redis' --type=string; \
|
||||
/web/nextcloud/occ config:system:set trusted_proxies 0 --value=fd4d:6169:6c63:6f77::1; \
|
||||
/web/nextcloud/occ config:system:set trusted_proxies 1 --value=172.22.1.0/24; \
|
||||
/web/nextcloud/occ config:system:set overwritewebroot --value=/nextcloud; \
|
||||
/web/nextcloud/occ config:system:set overwritehost --value=${MAILCOW_HOSTNAME}; \
|
||||
/web/nextcloud/occ config:system:set mail_smtpmode --value=smtp; \
|
||||
/web/nextcloud/occ config:system:set mail_smtpauthtype --value=LOGIN; \
|
||||
/web/nextcloud/occ config:system:set mail_from_address --value=nextcloud; \
|
||||
/web/nextcloud/occ config:system:set mail_domain --value=${MAILCOW_HOSTNAME}; \
|
||||
/web/nextcloud/occ config:system:set mail_smtphost --value=postfix; \
|
||||
/web/nextcloud/occ config:system:set mail_smtpport --value=588
|
||||
/web/nextcloud/occ app:enable user_external
|
||||
/web/nextcloud/occ config:system:set user_backends 0 arguments 0 --value={dovecot:143/imap/tls/novalidate-cert}
|
||||
/web/nextcloud/occ config:system:set user_backends 0 class --value=OC_User_IMAP"
|
||||
|
||||
if [[ ${NC_TYPE} == "subdomain" ]]; then
|
||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:set overwritewebroot --value=/
|
||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:set overwritehost --value=nextcloud.develcow.de
|
||||
cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/
|
||||
sed -i 's/NC_SUBD/${NC_SUBD}/g' ./data/conf/nginx/nextcloud.conf
|
||||
elif [[ ${NC_TYPE} == "subfolder" ]]; then
|
||||
cp ./data/assets/nextcloud/site.nextcloud.custom ./data/conf/nginx/
|
||||
fi
|
||||
|
||||
docker-compose restart nginx-mailcow
|
||||
|
||||
echo "Login as admin with password: ${ADMIN_NC_PASS}"
|
||||
|
||||
fi
|
Loading…
Reference in New Issue