Merge branch 'master' into master
commit
9750ec5bec
|
@ -1,10 +1,12 @@
|
|||
rebuild-images.sh
|
||||
data/conf/sogo/sieve.creds
|
||||
data/conf/dovecot/dovecot-master.passwd
|
||||
data/conf/dovecot/dovecot-master.userdb
|
||||
mailcow.conf
|
||||
mailcow.conf_backup
|
||||
data/conf/nginx/*.active
|
||||
data/conf/postfix/sql
|
||||
data/conf/postfix/allow_mailcow_local.regexp
|
||||
data/conf/dovecot/sql
|
||||
data/conf/nextcloud-*.bak
|
||||
data/web/inc/vars.local.inc.php
|
||||
|
|
|
@ -117,7 +117,7 @@ RAND_USER=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 16 | head -n 1)
|
|||
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 24 | head -n 1)
|
||||
|
||||
echo ${RAND_USER}@mailcow.local:{SHA1}$(echo -n ${RAND_PASS} | sha1sum | awk '{print $1}') > /usr/local/etc/dovecot/dovecot-master.passwd
|
||||
|
||||
echo ${RAND_USER}@mailcow.local::5000:5000:::: > /usr/local/etc/dovecot/dovecot-master.userdb
|
||||
echo ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/sieve.creds
|
||||
|
||||
# 401 is user dovecot
|
||||
|
|
|
@ -13,3 +13,6 @@ catch_non_zero "/usr/bin/redis-cli -h redis LTRIM DOVECOT_MAILLOG 0 LOG_LINES"
|
|||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM SOGO_LOG 0 LOG_LINES"
|
||||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM NETFILTER_LOG 0 LOG_LINES"
|
||||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM AUTODISCOVER_LOG 0 LOG_LINES"
|
||||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM API_LOG 0 LOG_LINES"
|
||||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM RL_LOG 0 LOG_LINES"
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
FROM php:7.2-fpm-alpine3.8
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
ENV APCU_PECL 5.1.12
|
||||
ENV APCU_PECL 5.1.16
|
||||
ENV IMAGICK_PECL 3.4.3
|
||||
ENV MAILPARSE_PECL 3.0.2
|
||||
ENV MEMCACHED_PECL 3.0.4
|
||||
ENV REDIS_PECL 4.1.1
|
||||
ENV MEMCACHED_PECL 3.1.3
|
||||
ENV REDIS_PECL 4.2.0
|
||||
|
||||
RUN apk add -U --no-cache autoconf \
|
||||
bash \
|
||||
|
|
|
@ -23,12 +23,25 @@ fi
|
|||
# Check of mysql_upgrade
|
||||
|
||||
CONTAINER_ID=
|
||||
# Todo: Better check if upgrade failed
|
||||
# This can happen due to a broken sogo_view
|
||||
[ -s /mysql_upgrade_loop ] && SQL_LOOP_C=$(cat /mysql_upgrade_loop)
|
||||
CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"mysql-mailcow\")) | .id")
|
||||
if [[ ! -z ${CONTAINER_ID} ]]; then
|
||||
if [[ ! -z "${CONTAINER_ID}" ]] && [[ "${CONTAINER_ID}" =~ [^a-zA-Z0-9] ]]; then
|
||||
SQL_UPGRADE_RETURN=$(curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_upgrade"}' --silent -H 'Content-type: application/json' | jq -r .type)
|
||||
if [[ ${SQL_UPGRADE_RETURN} == 'warning' ]]; then
|
||||
echo "MySQL applied an upgrade, restarting PHP-FPM..."
|
||||
exit 1
|
||||
if [ -z ${SQL_LOOP_C} ]; then
|
||||
echo 1 > /mysql_upgrade_loop
|
||||
echo "MySQL applied an upgrade, restarting PHP-FPM..."
|
||||
exit 1
|
||||
else
|
||||
rm /mysql_upgrade_loop
|
||||
echo "MySQL was not applied previously, skipping. Restart php-fpm-mailcow to retry or run mysql_upgrade manually."
|
||||
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
|
||||
echo "Waiting for SQL to return..."
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
@ -85,7 +85,17 @@ query = SELECT GROUP_CONCAT(transport SEPARATOR '') AS transport_maps
|
|||
AS transport_view;
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_passwd_maps.cf
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_transport_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = unix:/var/run/mysqld/mysqld.sock
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT CONCAT('smtp_via_transport_maps:', nexthop) AS transport FROM transports
|
||||
WHERE active = '1'
|
||||
AND destination = '%s';
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = unix:/var/run/mysqld/mysqld.sock
|
||||
|
@ -98,9 +108,22 @@ query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM relayhosts
|
|||
SELECT CONCAT('@', alias_domain) FROM alias_domain
|
||||
)
|
||||
)
|
||||
AND active = '1'
|
||||
AND username != '';
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = unix:/var/run/mysqld/mysqld.sock
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM transports
|
||||
WHERE nexthop = '%s'
|
||||
AND active = '1'
|
||||
AND username != ''
|
||||
LIMIT 1;
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_domain_catchall_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
|
|
|
@ -21,6 +21,7 @@ RUN apt-get update && apt-get install -y \
|
|||
|
||||
COPY settings.conf /etc/rspamd/settings.conf
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
COPY ratelimit.lua /usr/share/rspamd/lua/ratelimit.lua
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
|
|
|
@ -0,0 +1,864 @@
|
|||
--[[
|
||||
Copyright (c) 2011-2017, Vsevolod Stakhov <vsevolod@highsecure.ru>
|
||||
Copyright (c) 2016-2017, Andrew Lewis <nerf@judo.za.org>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
]]--
|
||||
|
||||
if confighelp then
|
||||
return
|
||||
end
|
||||
|
||||
local rspamd_logger = require "rspamd_logger"
|
||||
local rspamd_util = require "rspamd_util"
|
||||
local rspamd_lua_utils = require "lua_util"
|
||||
local lua_redis = require "lua_redis"
|
||||
local fun = require "fun"
|
||||
local lua_maps = require "lua_maps"
|
||||
local lua_util = require "lua_util"
|
||||
local rspamd_hash = require "rspamd_cryptobox_hash"
|
||||
local lua_selectors = require "lua_selectors"
|
||||
local ts = require("tableshape").types
|
||||
|
||||
-- A plugin that implements ratelimits using redis
|
||||
|
||||
local E = {}
|
||||
local N = 'ratelimit'
|
||||
local redis_params
|
||||
-- Senders that are considered as bounce
|
||||
local settings = {
|
||||
bounce_senders = { 'postmaster', 'mailer-daemon', '', 'null', 'fetchmail-daemon', 'mdaemon' },
|
||||
-- Do not check ratelimits for these recipients
|
||||
whitelisted_rcpts = { 'postmaster', 'mailer-daemon' },
|
||||
prefix = 'RL',
|
||||
ham_factor_rate = 1.01,
|
||||
spam_factor_rate = 0.99,
|
||||
ham_factor_burst = 1.02,
|
||||
spam_factor_burst = 0.98,
|
||||
max_rate_mult = 5,
|
||||
max_bucket_mult = 10,
|
||||
expire = 60 * 60 * 24 * 2, -- 2 days by default
|
||||
limits = {},
|
||||
allow_local = false,
|
||||
}
|
||||
|
||||
-- Checks bucket, updating it if needed
|
||||
-- KEYS[1] - prefix to update, e.g. RL_<triplet>_<seconds>
|
||||
-- KEYS[2] - current time in milliseconds
|
||||
-- KEYS[3] - bucket leak rate (messages per millisecond)
|
||||
-- KEYS[4] - bucket burst
|
||||
-- KEYS[5] - expire for a bucket
|
||||
-- return 1 if message should be ratelimited and 0 if not
|
||||
-- Redis keys used:
|
||||
-- l - last hit
|
||||
-- b - current burst
|
||||
-- dr - current dynamic rate multiplier (*10000)
|
||||
-- db - current dynamic burst multiplier (*10000)
|
||||
local bucket_check_script = [[
|
||||
local last = redis.call('HGET', KEYS[1], 'l')
|
||||
local now = tonumber(KEYS[2])
|
||||
local dynr, dynb, leaked = 0, 0, 0
|
||||
if not last then
|
||||
-- New bucket
|
||||
redis.call('HSET', KEYS[1], 'l', KEYS[2])
|
||||
redis.call('HSET', KEYS[1], 'b', '0')
|
||||
redis.call('HSET', KEYS[1], 'dr', '10000')
|
||||
redis.call('HSET', KEYS[1], 'db', '10000')
|
||||
redis.call('EXPIRE', KEYS[1], KEYS[5])
|
||||
return {0, '0', '1', '1', '0'}
|
||||
end
|
||||
|
||||
last = tonumber(last)
|
||||
local burst = tonumber(redis.call('HGET', KEYS[1], 'b'))
|
||||
-- Perform leak
|
||||
if burst > 0 then
|
||||
if last < tonumber(KEYS[2]) then
|
||||
local rate = tonumber(KEYS[3])
|
||||
dynr = tonumber(redis.call('HGET', KEYS[1], 'dr')) / 10000.0
|
||||
if dynr == 0 then dynr = 0.0001 end
|
||||
rate = rate * dynr
|
||||
leaked = ((now - last) * rate)
|
||||
if leaked > burst then leaked = burst end
|
||||
burst = burst - leaked
|
||||
redis.call('HINCRBYFLOAT', KEYS[1], 'b', -(leaked))
|
||||
redis.call('HSET', KEYS[1], 'l', KEYS[2])
|
||||
end
|
||||
|
||||
dynb = tonumber(redis.call('HGET', KEYS[1], 'db')) / 10000.0
|
||||
if dynb == 0 then dynb = 0.0001 end
|
||||
|
||||
if burst > 0 and (burst + 1) > tonumber(KEYS[4]) * dynb then
|
||||
return {1, tostring(burst), tostring(dynr), tostring(dynb), tostring(leaked)}
|
||||
end
|
||||
else
|
||||
burst = 0
|
||||
redis.call('HSET', KEYS[1], 'b', '0')
|
||||
end
|
||||
|
||||
return {0, tostring(burst), tostring(dynr), tostring(dynb), tostring(leaked)}
|
||||
]]
|
||||
local bucket_check_id
|
||||
|
||||
|
||||
-- Updates a bucket
|
||||
-- KEYS[1] - prefix to update, e.g. RL_<triplet>_<seconds>
|
||||
-- KEYS[2] - current time in milliseconds
|
||||
-- KEYS[3] - dynamic rate multiplier
|
||||
-- KEYS[4] - dynamic burst multiplier
|
||||
-- KEYS[5] - max dyn rate (min: 1/x)
|
||||
-- KEYS[6] - max burst rate (min: 1/x)
|
||||
-- KEYS[7] - expire for a bucket
|
||||
-- Redis keys used:
|
||||
-- l - last hit
|
||||
-- b - current burst
|
||||
-- dr - current dynamic rate multiplier
|
||||
-- db - current dynamic burst multiplier
|
||||
local bucket_update_script = [[
|
||||
local last = redis.call('HGET', KEYS[1], 'l')
|
||||
local now = tonumber(KEYS[2])
|
||||
if not last then
|
||||
-- New bucket
|
||||
redis.call('HSET', KEYS[1], 'l', KEYS[2])
|
||||
redis.call('HSET', KEYS[1], 'b', '1')
|
||||
redis.call('HSET', KEYS[1], 'dr', '10000')
|
||||
redis.call('HSET', KEYS[1], 'db', '10000')
|
||||
redis.call('EXPIRE', KEYS[1], KEYS[7])
|
||||
return {1, 1, 1}
|
||||
end
|
||||
|
||||
local dr, db = 1.0, 1.0
|
||||
|
||||
if tonumber(KEYS[5]) > 1 then
|
||||
local rate_mult = tonumber(KEYS[3])
|
||||
local rate_limit = tonumber(KEYS[5])
|
||||
dr = tonumber(redis.call('HGET', KEYS[1], 'dr')) / 10000
|
||||
|
||||
if rate_mult > 1.0 and dr < rate_limit then
|
||||
dr = dr * rate_mult
|
||||
if dr > 0.0001 then
|
||||
redis.call('HSET', KEYS[1], 'dr', tostring(math.floor(dr * 10000)))
|
||||
else
|
||||
redis.call('HSET', KEYS[1], 'dr', '1')
|
||||
end
|
||||
elseif rate_mult < 1.0 and dr > (1.0 / rate_limit) then
|
||||
dr = dr * rate_mult
|
||||
if dr > 0.0001 then
|
||||
redis.call('HSET', KEYS[1], 'dr', tostring(math.floor(dr * 10000)))
|
||||
else
|
||||
redis.call('HSET', KEYS[1], 'dr', '1')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if tonumber(KEYS[6]) > 1 then
|
||||
local rate_mult = tonumber(KEYS[4])
|
||||
local rate_limit = tonumber(KEYS[6])
|
||||
db = tonumber(redis.call('HGET', KEYS[1], 'db')) / 10000
|
||||
|
||||
if rate_mult > 1.0 and db < rate_limit then
|
||||
db = db * rate_mult
|
||||
if db > 0.0001 then
|
||||
redis.call('HSET', KEYS[1], 'db', tostring(math.floor(db * 10000)))
|
||||
else
|
||||
redis.call('HSET', KEYS[1], 'db', '1')
|
||||
end
|
||||
elseif rate_mult < 1.0 and db > (1.0 / rate_limit) then
|
||||
db = db * rate_mult
|
||||
if db > 0.0001 then
|
||||
redis.call('HSET', KEYS[1], 'db', tostring(math.floor(db * 10000)))
|
||||
else
|
||||
redis.call('HSET', KEYS[1], 'db', '1')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local burst = tonumber(redis.call('HGET', KEYS[1], 'b'))
|
||||
if burst < 0 then burst = 0 end
|
||||
|
||||
redis.call('HINCRBYFLOAT', KEYS[1], 'b', 1)
|
||||
redis.call('HSET', KEYS[1], 'l', KEYS[2])
|
||||
redis.call('EXPIRE', KEYS[1], KEYS[7])
|
||||
|
||||
return {tostring(burst), tostring(dr), tostring(db)}
|
||||
]]
|
||||
local bucket_update_id
|
||||
|
||||
-- message_func(task, limit_type, prefix, bucket, limit_key)
|
||||
local message_func = function(_, limit_type, _, _, _)
|
||||
return string.format('Ratelimit "%s" exceeded', limit_type)
|
||||
end
|
||||
|
||||
|
||||
local function load_scripts(cfg, ev_base)
|
||||
bucket_check_id = lua_redis.add_redis_script(bucket_check_script, redis_params)
|
||||
bucket_update_id = lua_redis.add_redis_script(bucket_update_script, redis_params)
|
||||
end
|
||||
|
||||
local limit_parser
|
||||
local function parse_string_limit(lim, no_error)
|
||||
local function parse_time_suffix(s)
|
||||
if s == 's' then
|
||||
return 1
|
||||
elseif s == 'm' then
|
||||
return 60
|
||||
elseif s == 'h' then
|
||||
return 3600
|
||||
elseif s == 'd' then
|
||||
return 86400
|
||||
end
|
||||
end
|
||||
local function parse_num_suffix(s)
|
||||
if s == '' then
|
||||
return 1
|
||||
elseif s == 'k' then
|
||||
return 1000
|
||||
elseif s == 'm' then
|
||||
return 1000000
|
||||
elseif s == 'g' then
|
||||
return 1000000000
|
||||
end
|
||||
end
|
||||
local lpeg = require "lpeg"
|
||||
|
||||
if not limit_parser then
|
||||
local digit = lpeg.R("09")
|
||||
limit_parser = {}
|
||||
limit_parser.integer =
|
||||
(lpeg.S("+-") ^ -1) *
|
||||
(digit ^ 1)
|
||||
limit_parser.fractional =
|
||||
(lpeg.P(".") ) *
|
||||
(digit ^ 1)
|
||||
limit_parser.number =
|
||||
(limit_parser.integer *
|
||||
(limit_parser.fractional ^ -1)) +
|
||||
(lpeg.S("+-") * limit_parser.fractional)
|
||||
limit_parser.time = lpeg.Cf(lpeg.Cc(1) *
|
||||
(limit_parser.number / tonumber) *
|
||||
((lpeg.S("smhd") / parse_time_suffix) ^ -1),
|
||||
function (acc, val) return acc * val end)
|
||||
limit_parser.suffixed_number = lpeg.Cf(lpeg.Cc(1) *
|
||||
(limit_parser.number / tonumber) *
|
||||
((lpeg.S("kmg") / parse_num_suffix) ^ -1),
|
||||
function (acc, val) return acc * val end)
|
||||
limit_parser.limit = lpeg.Ct(limit_parser.suffixed_number *
|
||||
(lpeg.S(" ") ^ 0) * lpeg.S("/") * (lpeg.S(" ") ^ 0) *
|
||||
limit_parser.time)
|
||||
end
|
||||
local t = lpeg.match(limit_parser.limit, lim)
|
||||
|
||||
if t and t[1] and t[2] and t[2] ~= 0 then
|
||||
return t[2], t[1]
|
||||
end
|
||||
|
||||
if not no_error then
|
||||
rspamd_logger.errx(rspamd_config, 'bad limit: %s', lim)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function str_to_rate(str)
|
||||
local divider,divisor = parse_string_limit(str, false)
|
||||
|
||||
if not divisor then
|
||||
rspamd_logger.errx(rspamd_config, 'bad rate string: %s', str)
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return divisor / divider
|
||||
end
|
||||
|
||||
local bucket_schema = ts.shape{
|
||||
burst = ts.number + ts.string / lua_util.dehumanize_number,
|
||||
rate = ts.number + ts.string / str_to_rate
|
||||
}
|
||||
|
||||
local function parse_limit(name, data)
|
||||
if type(data) == 'table' then
|
||||
-- 2 cases here:
|
||||
-- * old limit in format [burst, rate]
|
||||
-- * vector of strings in Andrew's string format (removed from 1.8.2)
|
||||
-- * proper bucket table
|
||||
if #data == 2 and tonumber(data[1]) and tonumber(data[2]) then
|
||||
-- Old style ratelimit
|
||||
rspamd_logger.warnx(rspamd_config, 'old style ratelimit for %s', name)
|
||||
if tonumber(data[1]) > 0 and tonumber(data[2]) > 0 then
|
||||
return {
|
||||
burst = data[1],
|
||||
rate = data[2]
|
||||
}
|
||||
elseif data[1] ~= 0 then
|
||||
rspamd_logger.warnx(rspamd_config, 'invalid numbers for %s', name)
|
||||
else
|
||||
rspamd_logger.infox(rspamd_config, 'disable limit %s, burst is zero', name)
|
||||
end
|
||||
|
||||
return nil
|
||||
else
|
||||
local parsed_bucket,err = bucket_schema:transform(data)
|
||||
|
||||
if not parsed_bucket or err then
|
||||
rspamd_logger.errx(rspamd_config, 'cannot parse bucket for %s: %s; original value: %s',
|
||||
name, err, data)
|
||||
else
|
||||
return parsed_bucket
|
||||
end
|
||||
end
|
||||
elseif type(data) == 'string' then
|
||||
local rep_rate, burst = parse_string_limit(data)
|
||||
rspamd_logger.warnx(rspamd_config, 'old style rate bucket config detected for %s: %s',
|
||||
name, data)
|
||||
if rep_rate and burst then
|
||||
return {
|
||||
burst = burst,
|
||||
rate = burst / rep_rate -- reciprocal
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Check whether this addr is bounce
|
||||
local function check_bounce(from)
|
||||
return fun.any(function(b) return b == from end, settings.bounce_senders)
|
||||
end
|
||||
|
||||
local keywords = {
|
||||
['ip'] = {
|
||||
['get_value'] = function(task)
|
||||
local ip = task:get_ip()
|
||||
if ip and ip:is_valid() then return tostring(ip) end
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
['rip'] = {
|
||||
['get_value'] = function(task)
|
||||
local ip = task:get_ip()
|
||||
if ip and ip:is_valid() and not ip:is_local() then return tostring(ip) end
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
['from'] = {
|
||||
['get_value'] = function(task)
|
||||
local from = task:get_from(0)
|
||||
if ((from or E)[1] or E).addr then
|
||||
return string.lower(from[1]['addr'])
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
['bounce'] = {
|
||||
['get_value'] = function(task)
|
||||
local from = task:get_from(0)
|
||||
if not ((from or E)[1] or E).user then
|
||||
return '_'
|
||||
end
|
||||
if check_bounce(from[1]['user']) then return '_' else return nil end
|
||||
end,
|
||||
},
|
||||
['asn'] = {
|
||||
['get_value'] = function(task)
|
||||
local asn = task:get_mempool():get_variable('asn')
|
||||
if not asn then
|
||||
return nil
|
||||
else
|
||||
return asn
|
||||
end
|
||||
end,
|
||||
},
|
||||
['user'] = {
|
||||
['get_value'] = function(task)
|
||||
local auser = task:get_user()
|
||||
if not auser then
|
||||
return nil
|
||||
else
|
||||
return auser
|
||||
end
|
||||
end,
|
||||
},
|
||||
['to'] = {
|
||||
['get_value'] = function(task)
|
||||
return task:get_principal_recipient()
|
||||
end,
|
||||
},
|
||||
['digest'] = {
|
||||
['get_value'] = function(task)
|
||||
return task:get_digest()
|
||||
end,
|
||||
},
|
||||
['attachments'] = {
|
||||
['get_value'] = function(task)
|
||||
local parts = task:get_parts() or E
|
||||
local digests = {}
|
||||
|
||||
for _,p in ipairs(parts) do
|
||||
if p:get_filename() then
|
||||
table.insert(digests, p:get_digest())
|
||||
end
|
||||
end
|
||||
|
||||
if #digests > 0 then
|
||||
return table.concat(digests, '')
|
||||
end
|
||||
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
['files'] = {
|
||||
['get_value'] = function(task)
|
||||
local parts = task:get_parts() or E
|
||||
local files = {}
|
||||
|
||||
for _,p in ipairs(parts) do
|
||||
local fname = p:get_filename()
|
||||
if fname then
|
||||
table.insert(files, fname)
|
||||
end
|
||||
end
|
||||
|
||||
if #files > 0 then
|
||||
return table.concat(files, ':')
|
||||
end
|
||||
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
local function gen_rate_key(task, rtype, bucket)
|
||||
local key_t = {tostring(lua_util.round(100000.0 / bucket.burst))}
|
||||
local key_keywords = lua_util.str_split(rtype, '_')
|
||||
local have_user = false
|
||||
|
||||
for _, v in ipairs(key_keywords) do
|
||||
local ret
|
||||
|
||||
if keywords[v] and type(keywords[v]['get_value']) == 'function' then
|
||||
ret = keywords[v]['get_value'](task)
|
||||
end
|
||||
if not ret then return nil end
|
||||
if v == 'user' then have_user = true end
|
||||
if type(ret) ~= 'string' then ret = tostring(ret) end
|
||||
table.insert(key_t, ret)
|
||||
end
|
||||
|
||||
if have_user and not task:get_user() then
|
||||
return nil
|
||||
end
|
||||
|
||||
return table.concat(key_t, ":")
|
||||
end
|
||||
|
||||
local function make_prefix(redis_key, name, bucket)
|
||||
local hash_len = 24
|
||||
if hash_len > #redis_key then hash_len = #redis_key end
|
||||
local hash = settings.prefix ..
|
||||
string.sub(rspamd_hash.create(redis_key):base32(), 1, hash_len)
|
||||
-- Fill defaults
|
||||
if not bucket.spam_factor_rate then
|
||||
bucket.spam_factor_rate = settings.spam_factor_rate
|
||||
end
|
||||
if not bucket.ham_factor_rate then
|
||||
bucket.ham_factor_rate = settings.ham_factor_rate
|
||||
end
|
||||
if not bucket.spam_factor_burst then
|
||||
bucket.spam_factor_burst = settings.spam_factor_burst
|
||||
end
|
||||
if not bucket.ham_factor_burst then
|
||||
bucket.ham_factor_burst = settings.ham_factor_burst
|
||||
end
|
||||
|
||||
return {
|
||||
bucket = bucket,
|
||||
name = name,
|
||||
hash = hash
|
||||
}
|
||||
end
|
||||
|
||||
local function limit_to_prefixes(task, k, v, prefixes)
|
||||
local n = 0
|
||||
for _,bucket in ipairs(v.buckets) do
|
||||
if v.selector then
|
||||
local selectors = lua_selectors.process_selectors(task, v.selector)
|
||||
if selectors then
|
||||
local combined = lua_selectors.combine_selectors(task, selectors, ':')
|
||||
if type(combined) == 'string' then
|
||||
prefixes[combined] = make_prefix(combined, k, bucket)
|
||||
n = n + 1
|
||||
else
|
||||
fun.each(function(p)
|
||||
prefixes[p] = make_prefix(p, k, bucket)
|
||||
n = n + 1
|
||||
end, combined)
|
||||
end
|
||||
end
|
||||
else
|
||||
local prefix = gen_rate_key(task, k, bucket)
|
||||
if prefix then
|
||||
if type(prefix) == 'string' then
|
||||
prefixes[prefix] = make_prefix(prefix, k, bucket)
|
||||
n = n + 1
|
||||
else
|
||||
fun.each(function(p)
|
||||
prefixes[p] = make_prefix(p, k, bucket)
|
||||
n = n + 1
|
||||
end, prefix)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return n
|
||||
end
|
||||
|
||||
local function ratelimit_cb(task)
|
||||
if not settings.allow_local and
|
||||
rspamd_lua_utils.is_rspamc_or_controller(task) then return end
|
||||
|
||||
-- Get initial task data
|
||||
local ip = task:get_from_ip()
|
||||
if ip and ip:is_valid() and settings.whitelisted_ip then
|
||||
if settings.whitelisted_ip:get_key(ip) then
|
||||
-- Do not check whitelisted ip
|
||||
rspamd_logger.infox(task, 'skip ratelimit for whitelisted IP')
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Parse all rcpts
|
||||
local rcpts = task:get_recipients()
|
||||
local rcpts_user = {}
|
||||
if rcpts then
|
||||
fun.each(function(r)
|
||||
fun.each(function(type) table.insert(rcpts_user, r[type]) end, {'user', 'addr'})
|
||||
end, rcpts)
|
||||
|
||||
if fun.any(function(r) return settings.whitelisted_rcpts:get_key(r) end, rcpts_user) then
|
||||
rspamd_logger.infox(task, 'skip ratelimit for whitelisted recipient')
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Get user (authuser)
|
||||
if settings.whitelisted_user then
|
||||
local auser = task:get_user()
|
||||
if settings.whitelisted_user:get_key(auser) then
|
||||
rspamd_logger.infox(task, 'skip ratelimit for whitelisted user')
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Now create all ratelimit prefixes
|
||||
local prefixes = {}
|
||||
local nprefixes = 0
|
||||
|
||||
for k,v in pairs(settings.limits) do
|
||||
nprefixes = nprefixes + limit_to_prefixes(task, k, v, prefixes)
|
||||
end
|
||||
|
||||
for k, hdl in pairs(settings.custom_keywords or E) do
|
||||
local ret, redis_key, bd = pcall(hdl, task)
|
||||
|
||||
if ret then
|
||||
local bucket = parse_limit(k, bd)
|
||||
if bucket then
|
||||
prefixes[redis_key] = make_prefix(redis_key, k, bucket)
|
||||
end
|
||||
nprefixes = nprefixes + 1
|
||||
else
|
||||
rspamd_logger.errx(task, 'cannot call handler for %s: %s',
|
||||
k, redis_key)
|
||||
end
|
||||
end
|
||||
|
||||
local function gen_check_cb(prefix, bucket, lim_name, lim_key)
|
||||
return function(err, data)
|
||||
if err then
|
||||
rspamd_logger.errx('cannot check limit %s: %s %s', prefix, err, data)
|
||||
elseif type(data) == 'table' and data[1] then
|
||||
lua_util.debugm(N, task,
|
||||
"got reply for limit %s (%s / %s); %s burst, %s:%s dyn, %s leaked",
|
||||
prefix, bucket.burst, bucket.rate,
|
||||
data[2], data[3], data[4], data[5])
|
||||
|
||||
if data[1] == 1 then
|
||||
-- set symbol only and do NOT soft reject
|
||||
if settings.symbol then
|
||||
task:insert_result(settings.symbol, 0.0,
|
||||
string.format('%s(%s)', lim_name, lim_key))
|
||||
rspamd_logger.infox(task,
|
||||
'set_symbol_only: ratelimit "%s(%s)" exceeded, (%s / %s): %s (%s:%s dyn); redis key: %s',
|
||||
lim_name, prefix,
|
||||
bucket.burst, bucket.rate,
|
||||
data[2], data[3], data[4], lim_key)
|
||||
return
|
||||
-- set INFO symbol and soft reject
|
||||
elseif settings.info_symbol then
|
||||
task:insert_result(settings.info_symbol, 1.0,
|
||||
string.format('%s(%s)', lim_name, lim_key))
|
||||
end
|
||||
rspamd_logger.infox(task,
|
||||
'ratelimit "%s(%s)" exceeded, (%s / %s): %s (%s:%s dyn); redis key: %s',
|
||||
lim_name, prefix,
|
||||
bucket.burst, bucket.rate,
|
||||
data[2], data[3], data[4], lim_key)
|
||||
task:set_pre_result('soft reject',
|
||||
message_func(task, lim_name, prefix, bucket, lim_key), N)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Don't do anything if pre-result has been already set
|
||||
if task:has_pre_result() then return end
|
||||
|
||||
if nprefixes > 0 then
|
||||
-- Save prefixes to the cache to allow update
|
||||
task:cache_set('ratelimit_prefixes', prefixes)
|
||||
local now = rspamd_util.get_time()
|
||||
now = lua_util.round(now * 1000.0) -- Get milliseconds
|
||||
-- Now call check script for all defined prefixes
|
||||
|
||||
for pr,value in pairs(prefixes) do
|
||||
local bucket = value.bucket
|
||||
local rate = (bucket.rate) / 1000.0 -- Leak rate in messages/ms
|
||||
lua_util.debugm(N, task, "check limit %s:%s -> %s (%s/%s)",
|
||||
value.name, pr, value.hash, bucket.burst, bucket.rate)
|
||||
lua_redis.exec_redis_script(bucket_check_id,
|
||||
{key = value.hash, task = task, is_write = true},
|
||||
gen_check_cb(pr, bucket, value.name, value.hash),
|
||||
{value.hash, tostring(now), tostring(rate), tostring(bucket.burst),
|
||||
tostring(settings.expire)})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function ratelimit_update_cb(task)
|
||||
if task:has_flag('skip') then return end
|
||||
if not settings.allow_local and lua_util.is_rspamc_or_controller(task) then return end
|
||||
local prefixes = task:cache_get('ratelimit_prefixes')
|
||||
|
||||
if prefixes then
|
||||
if task:has_pre_result() then
|
||||
-- Already rate limited/greylisted, do nothing
|
||||
lua_util.debugm(N, task, 'pre-action has been set, do not update')
|
||||
return
|
||||
end
|
||||
|
||||
local verdict = lua_util.get_task_verdict(task)
|
||||
|
||||
-- Update each bucket
|
||||
for k, v in pairs(prefixes) do
|
||||
local bucket = v.bucket
|
||||
local function update_bucket_cb(err, data)
|
||||
if err then
|
||||
rspamd_logger.errx(task, 'cannot update rate bucket %s: %s',
|
||||
k, err)
|
||||
else
|
||||
lua_util.debugm(N, task,
|
||||
"updated limit %s:%s -> %s (%s/%s), burst: %s, dyn_rate: %s, dyn_burst: %s",
|
||||
v.name, k, v.hash,
|
||||
bucket.burst, bucket.rate,
|
||||
data[1], data[2], data[3])
|
||||
end
|
||||
end
|
||||
local now = rspamd_util.get_time()
|
||||
now = lua_util.round(now * 1000.0) -- Get milliseconds
|
||||
local mult_burst = 1.0
|
||||
local mult_rate = 1.0
|
||||
|
||||
if verdict == 'spam' or verdict == 'junk' then
|
||||
mult_burst = bucket.spam_factor_burst or 1.0
|
||||
mult_rate = bucket.spam_factor_rate or 1.0
|
||||
elseif verdict == 'ham' then
|
||||
mult_burst = bucket.ham_factor_burst or 1.0
|
||||
mult_rate = bucket.ham_factor_rate or 1.0
|
||||
end
|
||||
|
||||
lua_redis.exec_redis_script(bucket_update_id,
|
||||
{key = v.hash, task = task, is_write = true},
|
||||
update_bucket_cb,
|
||||
{v.hash, tostring(now), tostring(mult_rate), tostring(mult_burst),
|
||||
tostring(settings.max_rate_mult), tostring(settings.max_bucket_mult),
|
||||
tostring(settings.expire)})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local opts = rspamd_config:get_all_opt(N)
|
||||
if opts then
|
||||
|
||||
settings = lua_util.override_defaults(settings, opts)
|
||||
|
||||
if opts['limit'] then
|
||||
rspamd_logger.errx(rspamd_config, 'Legacy ratelimit config format no longer supported')
|
||||
end
|
||||
|
||||
if opts['rates'] and type(opts['rates']) == 'table' then
|
||||
-- new way of setting limits
|
||||
fun.each(function(t, lim)
|
||||
local buckets = {}
|
||||
|
||||
if type(lim) == 'table' and lim.bucket then
|
||||
|
||||
if lim.bucket[1] then
|
||||
for _,bucket in ipairs(lim.bucket) do
|
||||
local b = parse_limit(t, bucket)
|
||||
|
||||
if not b then
|
||||
rspamd_logger.errx(rspamd_config, 'bad ratelimit bucket for %s: "%s"',
|
||||
t, b)
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(buckets, b)
|
||||
end
|
||||
else
|
||||
local bucket = parse_limit(t, lim.bucket)
|
||||
|
||||
if not bucket then
|
||||
rspamd_logger.errx(rspamd_config, 'bad ratelimit bucket for %s: "%s"',
|
||||
t, lim.bucket)
|
||||
return
|
||||
end
|
||||
|
||||
buckets = {bucket}
|
||||
end
|
||||
|
||||
settings.limits[t] = {
|
||||
buckets = buckets
|
||||
}
|
||||
|
||||
if lim.selector then
|
||||
local selector = lua_selectors.parse_selector(rspamd_config, lim.selector)
|
||||
if not selector then
|
||||
rspamd_logger.errx(rspamd_config, 'bad ratelimit selector for %s: "%s"',
|
||||
t, lim.selector)
|
||||
settings.limits[t] = nil
|
||||
return
|
||||
end
|
||||
|
||||
settings.limits[t].selector = selector
|
||||
end
|
||||
else
|
||||
rspamd_logger.warnx(rspamd_config, 'old syntax for ratelimits: %s', lim)
|
||||
buckets = parse_limit(t, lim)
|
||||
if buckets then
|
||||
settings.limits[t] = {
|
||||
buckets = {buckets}
|
||||
}
|
||||
end
|
||||
end
|
||||
end, opts['rates'])
|
||||
end
|
||||
|
||||
-- Display what's enabled
|
||||
fun.each(function(s)
|
||||
rspamd_logger.infox(rspamd_config, 'enabled ratelimit: %s', s)
|
||||
end, fun.map(function(n,d)
|
||||
return string.format('%s [%s]', n,
|
||||
table.concat(fun.totable(fun.map(function(v)
|
||||
return string.format('%s msgs burst, %s msgs/sec rate',
|
||||
v.burst, v.rate)
|
||||
end, d.buckets)), '; ')
|
||||
)
|
||||
end, settings.limits))
|
||||
|
||||
-- Ret, ret, ret: stupid legacy stuff:
|
||||
-- If we have a string with commas then load it as as static map
|
||||
-- otherwise, apply normal logic of Rspamd maps
|
||||
|
||||
local wrcpts = opts['whitelisted_rcpts']
|
||||
if type(wrcpts) == 'string' then
|
||||
if string.find(wrcpts, ',') then
|
||||
settings.whitelisted_rcpts = lua_maps.rspamd_map_add_from_ucl(
|
||||
lua_util.rspamd_str_split(wrcpts, ','), 'set', 'Ratelimit whitelisted rcpts')
|
||||
else
|
||||
settings.whitelisted_rcpts = lua_maps.rspamd_map_add_from_ucl(wrcpts, 'set',
|
||||
'Ratelimit whitelisted rcpts')
|
||||
end
|
||||
elseif type(opts['whitelisted_rcpts']) == 'table' then
|
||||
settings.whitelisted_rcpts = lua_maps.rspamd_map_add_from_ucl(wrcpts, 'set',
|
||||
'Ratelimit whitelisted rcpts')
|
||||
else
|
||||
-- Stupid default...
|
||||
settings.whitelisted_rcpts = lua_maps.rspamd_map_add_from_ucl(
|
||||
settings.whitelisted_rcpts, 'set', 'Ratelimit whitelisted rcpts')
|
||||
end
|
||||
|
||||
if opts['whitelisted_ip'] then
|
||||
settings.whitelisted_ip = lua_maps.rspamd_map_add('ratelimit', 'whitelisted_ip', 'radix',
|
||||
'Ratelimit whitelist ip map')
|
||||
end
|
||||
|
||||
if opts['whitelisted_user'] then
|
||||
settings.whitelisted_user = lua_maps.rspamd_map_add('ratelimit', 'whitelisted_user', 'set',
|
||||
'Ratelimit whitelist user map')
|
||||
end
|
||||
|
||||
settings.custom_keywords = {}
|
||||
if opts['custom_keywords'] then
|
||||
local ret, res_or_err = pcall(loadfile(opts['custom_keywords']))
|
||||
|
||||
if ret then
|
||||
opts['custom_keywords'] = {}
|
||||
if type(res_or_err) == 'table' then
|
||||
for k,hdl in pairs(res_or_err) do
|
||||
settings['custom_keywords'][k] = hdl
|
||||
end
|
||||
elseif type(res_or_err) == 'function' then
|
||||
settings['custom_keywords']['custom'] = res_or_err
|
||||
end
|
||||
else
|
||||
rspamd_logger.errx(rspamd_config, 'cannot execute %s: %s',
|
||||
opts['custom_keywords'], res_or_err)
|
||||
settings['custom_keywords'] = {}
|
||||
end
|
||||
end
|
||||
|
||||
if opts['message_func'] then
|
||||
message_func = assert(load(opts['message_func']))()
|
||||
end
|
||||
|
||||
redis_params = lua_redis.parse_redis_server('ratelimit')
|
||||
|
||||
if not redis_params then
|
||||
rspamd_logger.infox(rspamd_config, 'no servers are specified, disabling module')
|
||||
lua_util.disable_module(N, "redis")
|
||||
else
|
||||
local s = {
|
||||
type = 'prefilter,nostat',
|
||||
name = 'RATELIMIT_CHECK',
|
||||
priority = 7,
|
||||
callback = ratelimit_cb,
|
||||
flags = 'empty',
|
||||
}
|
||||
|
||||
if settings.symbol then
|
||||
s.name = settings.symbol
|
||||
elseif settings.info_symbol then
|
||||
s.name = settings.info_symbol
|
||||
end
|
||||
|
||||
rspamd_config:register_symbol(s)
|
||||
rspamd_config:register_symbol {
|
||||
type = 'idempotent',
|
||||
name = 'RATELIMIT_UPDATE',
|
||||
callback = ratelimit_update_cb,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
rspamd_config:add_on_load(function(cfg, ev_base, worker)
|
||||
load_scripts(cfg, ev_base)
|
||||
end)
|
|
@ -36,7 +36,7 @@ RUN mkdir /usr/share/doc/sogo \
|
|||
sogo \
|
||||
sogo-activesync \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& echo '* * * * * sogo /usr/sbin/sogo-ealarms-notify 2>/dev/null' > /etc/cron.d/sogo \
|
||||
&& echo '* * * * * sogo /usr/sbin/sogo-ealarms-notify -p /etc/sogo/sieve.creds 2>/dev/null' > /etc/cron.d/sogo \
|
||||
&& echo '* * * * * sogo /usr/sbin/sogo-tool expire-sessions 60' >> /etc/cron.d/sogo \
|
||||
&& echo '0 0 * * * sogo /usr/sbin/sogo-tool update-autoreply -p /etc/sogo/sieve.creds' >> /etc/cron.d/sogo \
|
||||
&& touch /etc/default/locale
|
||||
|
@ -44,9 +44,6 @@ RUN mkdir /usr/share/doc/sogo \
|
|||
COPY ./bootstrap-sogo.sh /bootstrap-sogo.sh
|
||||
COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf
|
||||
COPY supervisord.conf /etc/supervisor/supervisord.conf
|
||||
COPY theme-blue.js /usr/lib/GNUstep/SOGo/WebServerResources/js/theme-blue.js
|
||||
COPY theme-blue.css /usr/lib/GNUstep/SOGo/WebServerResources/css/theme-default.css
|
||||
COPY sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg
|
||||
COPY acl.diff /acl.diff
|
||||
COPY stop-supervisor.sh /usr/local/sbin/stop-supervisor.sh
|
||||
|
||||
|
|
|
@ -167,43 +167,6 @@ echo ' </dict>
|
|||
chown sogo:sogo -R /var/lib/sogo/
|
||||
chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
||||
|
||||
# Prevent theme switching
|
||||
sed -i \
|
||||
-e 's/eaf5e9/E3F2FD/g' \
|
||||
-e 's/cbe5c8/BBDEFB/g' \
|
||||
-e 's/aad6a5/90CAF9/g' \
|
||||
-e 's/88c781/64B5F6/g' \
|
||||
-e 's/66b86a/42A5F5/g' \
|
||||
-e 's/56b04c/2196F3/g' \
|
||||
-e 's/4da143/1E88E5/g' \
|
||||
-e 's/388e3c/1976D2/g' \
|
||||
-e 's/367d2e/1565C0/g' \
|
||||
-e 's/225e1b/0D47A1/g' \
|
||||
-e 's/fafafa/82B1FF/g' \
|
||||
-e 's/69f0ae/448AFF/g' \
|
||||
-e 's/00e676/2979ff/g' \
|
||||
-e 's/00c853/2962ff/g' \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/js/Common/Common.app.js \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/js/Common.js
|
||||
|
||||
sed -i \
|
||||
-e 's/default: "900"/default: "700"/g' \
|
||||
-e 's/default: "500"/default: "700"/g' \
|
||||
-e 's/"hue-1": "400"/"hue-1": "500"/g' \
|
||||
-e 's/"hue-1": "A100"/"hue-1": "500"/g' \
|
||||
-e 's/"hue-2": "800"/"hue-2": "700"/g' \
|
||||
-e 's/"hue-2": "300"/"hue-2": "700"/g' \
|
||||
-e 's/"hue-3": "A700"/"hue-3": "A200"/' \
|
||||
-e 's/default:"900"/default:"700"/g' \
|
||||
-e 's/default:"500"/default:"700"/g' \
|
||||
-e 's/"hue-1":"400"/"hue-1":"500"/g' \
|
||||
-e 's/"hue-1":"A100"/"hue-1":"500"/g' \
|
||||
-e 's/"hue-2":"800"/"hue-2":"700"/g' \
|
||||
-e 's/"hue-2":"300"/"hue-2":"700"/g' \
|
||||
-e 's/"hue-3":"A700"/"hue-3":"A200"/' \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/js/Common/Common.app.js \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/js/Common.js
|
||||
|
||||
# Patch ACLs
|
||||
if [[ ${ACL_ANYONE} == 'allow' ]]; then
|
||||
#enable any or authenticated targets for ACL
|
||||
|
@ -217,4 +180,7 @@ else
|
|||
fi
|
||||
fi
|
||||
|
||||
# Copy logo, if any
|
||||
[[ -f /etc/sogo/sogo-full.svg ]] && cp /etc/sogo/sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg
|
||||
|
||||
exec gosu sogo /usr/sbin/sogod
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="640px"
|
||||
height="350px"
|
||||
viewBox="78.712 58.488 640 350"
|
||||
style="enable-background:new 78.712 58.488 640 350;"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="sogo-full.svg"><metadata
|
||||
id="metadata9"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs7" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1721"
|
||||
inkscape:window-height="1177"
|
||||
id="namedview5"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.8396893"
|
||||
inkscape:cx="360.23913"
|
||||
inkscape:cy="334.02085"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" /><path
|
||||
style="fill:#1976d2;fill-opacity:0.71428573"
|
||||
d="M648.541,145.679c-9.947,0-17.009-7.278-17.009-17.048c0-9.777,7.062-17.057,17.009-17.057 c10.024,0,17.086,7.279,17.086,17.057C665.627,138.401,658.565,145.679,648.541,145.679z M648.511,94.893 c-19.693,0-33.679,14.4-33.679,33.738c0,19.33,13.985,33.729,33.679,33.729c19.822,0,33.808-14.4,33.808-33.729 C682.318,109.293,668.333,94.893,648.511,94.893z M648.482,179.843c-29.889,0-51.123-21.868-51.123-51.212 c0-29.353,21.234-51.209,51.123-51.209c30.082,0,51.307,21.856,51.307,51.209C699.789,157.975,678.564,179.843,648.482,179.843z M648.442,58.488c-40.929,0-69.995,29.946-69.995,70.143c0,40.189,29.066,70.125,69.995,70.125c41.194,0,70.27-29.937,70.27-70.125 C718.712,88.434,689.637,58.488,648.442,58.488z M158.166,183.902l-21.018-5.008c-19.131-4.396-28.849-9.413-28.849-23.21 c0-15.684,15.99-21.965,30.419-21.965c14.667,0,25.382,7.329,31.693,18.737c0.02,0.048,0.051,0.097,0.09,0.157 c0.127,0.247,0.276,0.484,0.403,0.731l0.03-0.02c1.985,3.002,5.323,5.008,8.919,5.008c6.122,0,10.558-4.425,10.558-10.547 c0-2.341-0.504-4.82-1.601-6.688c-10.764-18.302-28.513-26.192-48.838-26.192c-27.594,0-54.262,13.797-54.262,44.218 c0,27.921,27.605,36.079,37.64,38.578l20.069,4.71c15.368,3.763,27.912,8.791,27.912,23.517c0,16.938-17.561,23.943-34.499,23.943 c-17.245,0-30.015-9.37-38.814-22.37h-0.01c-1.956-3-4.988-4.328-8.702-4.328c-5.984,0-10.805,5.185-10.587,11.162 c0.098,2.438,0.909,4.637,2.153,6.405c13.787,20.633,33.728,28.41,55.96,28.41c28.543,0,57.085-13.143,57.085-45.132 C193.918,203.325,178.551,188.613,158.166,183.902z M298.479,250.312c-33.866,0-55.199-25.403-55.199-58.331 c0-32.939,21.333-58.343,55.199-58.343c34.192,0,55.516,25.403,55.516,58.343C353.996,224.91,332.672,250.312,298.479,250.312z M298.479,114.823c-45.471,0-77.777,32.93-77.777,77.158c0,44.217,32.306,77.146,77.777,77.146 c45.786,0,78.093-32.929,78.093-77.146C376.572,147.753,344.266,114.823,298.479,114.823z M518.715,234.312 c-0.771,0.74-1.549,1.472-2.399,2.175c-1.106,1.014-2.391,2.112-3.854,3.208c-8.829,6.391-19.979,10.094-33.017,10.094 c-33.876,0-55.198-25.402-55.198-58.332c0-32.939,21.322-58.342,55.198-58.342c34.183,0,55.506,25.403,55.506,58.342 C534.951,208.653,529.135,223.774,518.715,234.312z M468.097,317.938c2.528,0,5.146-0.168,7.863-0.504 c5.018-0.631,9.588-0.909,13.729-0.909c19.24,0.109,29.036,5.7,34.943,12.158c5.895,6.499,8.168,15.311,8.158,22.796 c0.01,3.586-0.555,6.795-1.177,8.721c-2.944,8.93-8.888,15.002-17.996,19.576c-9.035,4.484-21.095,6.777-33.707,6.757 c-4.514,0-9.105-0.288-13.639-0.831c-8.573-0.987-19.911-4.671-28.13-11.093c-4.138-3.199-6.458-6.991-8.858-11.485 c-2.379-4.514-2.783-9.748-2.783-16.442v-0.742c0-12.346,4.84-20.544,11.051-26.5c3.07-2.904,5.69-5.064,7.99-6.438 c0.366-0.218,0.438-0.416,0.755-0.593C452.39,316.014,459.684,317.968,468.097,317.938z M479.445,114.301 c-45.471,0-77.786,32.929-77.786,77.157c0,29.887,14.765,54.598,38.378,67.489c-0.314,0.314-0.621,0.641-0.916,0.966 c-6.104,6.687-9.226,15.25-9.236,23.913c-0.008,3.821,0.624,7.741,1.977,11.494c-3.062,1.956-6.717,4.634-10.46,8.147 c-9.026,8.408-18.734,22.541-19.021,42.097c-0.01,0.454-0.01,0.829-0.01,1.118c-0.01,10.071,2.379,19.157,6.459,26.774 c6.133,11.466,15.683,19.445,25.539,24.77c9.917,5.334,20.257,8.166,29.273,9.274c5.373,0.643,10.826,0.988,16.268,0.988 c15.151-0.02,30.261-2.578,43.409-9.019c13.085-6.34,24.333-17.253,29.192-32.562c1.443-4.553,2.212-9.719,2.231-15.428 c-0.02-11.595-3.349-25.759-13.767-37.452c-10.421-11.734-27.654-19.566-51.288-19.459c-5.138,0-10.606,0.356-16.426,1.078 c-1.877,0.227-3.596,0.334-5.166,0.334c-7.239-0.048-10.872-2.053-13.036-4.098c-2.133-2.084-3.2-4.839-3.229-8.058 c-0.01-3.28,1.284-6.727,3.467-9.078c2.231-2.332,5.008-3.91,9.846-3.97c0.436,0,0.9,0.01,1.374,0.05 c3.101,0.216,6.112,0.325,9.037,0.325c24.188,0.047,42.38-7.448,54.756-17.759c12.415-10.312,18.971-22.854,22.071-32.76l-0.04-0.01 c3.37-8.899,5.197-18.715,5.197-29.166C557.539,147.229,525.234,114.301,479.445,114.301z"
|
||||
id="path3" /><g
|
||||
id="g3"
|
||||
transform="matrix(0.69327133,0,0,0.69327133,-230.59227,-153.05511)"><g
|
||||
id="g5"><g
|
||||
id="g7"><path
|
||||
d="m 748.616,546.705 c -6.272,4.929 9.576,36.937 20.52,33.516 10.944,-3.42 -10.945,-41.04 -20.52,-33.516 z"
|
||||
id="path19"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#acef29" /></g></g><g
|
||||
id="g37"><g
|
||||
id="g39"><g
|
||||
id="g41" /></g><g
|
||||
id="g45"><g
|
||||
id="g47"><g
|
||||
id="g49" /></g></g></g><g
|
||||
id="g57"><g
|
||||
id="g59"><g
|
||||
id="g61" /></g><g
|
||||
id="g65"><g
|
||||
id="g67"><g
|
||||
id="g69" /></g></g></g><g
|
||||
id="g73"
|
||||
style="opacity:0.38999999"><g
|
||||
id="g75" /></g><g
|
||||
id="g81" /><g
|
||||
id="g85" /><g
|
||||
id="g99"><polyline
|
||||
points="690.928,453.92 678.401,490.532 689.894,539.76 710.135,559.116 "
|
||||
id="polyline101"
|
||||
style="fill:#3d5263" /><g
|
||||
id="g103"><g
|
||||
id="g105"><polyline
|
||||
points="665.082,423.023 677.433,450.19 693.064,457.2 715.139,427.604 "
|
||||
id="polyline107"
|
||||
style="fill:#fef3df" /><g
|
||||
id="g109"><path
|
||||
d="m 705.288,438.868 c 0,0 -17.599,-18.89 -38.165,-13.309 0,0 4.277,25.767 36.096,32.372 l 2.164,6.413 c 0,0 -49.958,-5.925 -46.456,-49.964 0,0 36.728,-16.138 55.372,9.881"
|
||||
id="path111"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b58765" /><polyline
|
||||
points="855.735,417.576 844.957,445.404 829.753,453.295 806.023,425.008 "
|
||||
id="polyline113"
|
||||
style="fill:#fef3df" /><path
|
||||
d="m 816.503,435.691 c 0,0 16.491,-19.864 37.34,-15.466 0,0 -2.797,25.969 -34.187,34.38 l -1.796,6.527 c 0,0 49.537,-8.768 43.528,-52.535 0,0 -37.588,-14.015 -54.719,13.026"
|
||||
id="path115"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b58765" /></g></g><path
|
||||
d="m 721.173,570.346 42.418,-1.212 15.59845,-160.5887 c -42.319,1.209 -92.18245,30.1357 -91.14645,66.4047 0.031,1.102 0.125,2.111 0.182,3.163 0.181,2.98 0.504,5.741 0.89,8.424 1.602,11.197 4.722,20.488 7.355,32.71 1.27,5.906 4.299,17.614 4.299,17.614 0.052,0.308 0.143,0.606 0.196,0.913 2.281,12.491 9.666,24.028 20.208,32.572 z"
|
||||
id="path117"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b58765"
|
||||
sodipodi:nodetypes="cccccccccc" /><path
|
||||
d="m 758.532,407.21 4.626,161.937 42.418,-1.212 c 10.038,-9.132 16.75,-21.072 18.317,-33.672 0.035,-0.31 0.107,-0.613 0.141,-0.923 0,0 2.354,-11.862 3.287,-17.831 1.932,-12.352 4.518,-21.807 5.478,-33.075 0.23,-2.702 0.393,-5.477 0.4,-8.463 0.002,-1.053 0.036,-2.066 0.005,-3.168 -1.037,-36.269 -32.35,-64.802 -74.672,-63.593 z"
|
||||
id="path119"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b58765" /><g
|
||||
id="g121"><g
|
||||
id="g123"><path
|
||||
d="m 822.293,541.988 c 0.613,21.473 -25.496,39.648 -58.32,40.586 -32.83,0.938 -59.932,-15.717 -60.546,-37.19 -0.614,-21.477 25.494,-39.648 58.324,-40.586 32.825,-0.938 59.929,15.713 60.542,37.19 z"
|
||||
id="path125"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#fef3df" /></g></g><g
|
||||
id="g127"><g
|
||||
id="g129"><g
|
||||
id="g131"><path
|
||||
d="m 735.761,538.45 c 0.135,4.712 -3.578,8.644 -8.294,8.778 -4.708,0.134 -8.641,-3.579 -8.776,-8.291 -0.135,-4.718 3.58,-8.644 8.288,-8.779 4.717,-0.134 8.647,3.573 8.782,8.292 z"
|
||||
id="path133"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#5a3620" /></g></g><g
|
||||
id="g135"><g
|
||||
id="g137"><path
|
||||
d="m 806.891,536.418 c 0.135,4.712 -3.575,8.644 -8.291,8.778 -4.714,0.135 -8.646,-3.579 -8.781,-8.291 -0.135,-4.718 3.579,-8.644 8.293,-8.779 4.716,-0.134 8.644,3.573 8.779,8.292 z"
|
||||
id="path139"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#5a3620" /></g></g></g><g
|
||||
id="g141"><path
|
||||
d="m 831.225,475.208 c 0.201,-2.368 0.344,-4.799 0.352,-7.411 0,-0.924 0.031,-1.81 0.003,-2.778 -0.883,-30.924 -26.904,-55.418 -62.478,-55.716 l -5.561,0.163 -0.005,0 c -0.005,0.014 -19.18666,70.69971 61.71134,107.73071 0.624,-3.294 9.87079,-9.74237 10.28979,-12.41137 1.694,-10.822 -5.15313,-19.70834 -4.31213,-29.57734 z"
|
||||
id="path143"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#87654a"
|
||||
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||
id="g145"><g
|
||||
id="g147"><g
|
||||
id="g149"><g
|
||||
id="g151"><path
|
||||
d="m 807.344,471.221 c 0.151,5.28 -4.011,9.684 -9.294,9.835 -5.279,0.151 -9.686,-4.008 -9.837,-9.288 -0.151,-5.285 4.011,-9.687 9.291,-9.838 5.282,-0.151 9.689,4.006 9.84,9.291 z"
|
||||
id="path153"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#5a3620" /></g></g></g><g
|
||||
id="g155"><g
|
||||
id="g157"><g
|
||||
id="g159"><path
|
||||
d="m 737.68,473.211 c 0.151,5.28 -4.01,9.684 -9.289,9.835 -5.284,0.151 -9.685,-4.008 -9.835,-9.288 -0.151,-5.285 4.005,-9.687 9.289,-9.837 5.279,-0.152 9.684,4.005 9.835,9.29 z"
|
||||
id="path161"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#5a3620" /></g></g></g><g
|
||||
id="g163"><g
|
||||
id="g165"><path
|
||||
d="m 735.112,470.41 c 0.055,1.939 -1.47,3.555 -3.41,3.61 -1.939,0.055 -3.558,-1.47 -3.613,-3.41 -0.055,-1.935 1.474,-3.552 3.413,-3.607 1.94,-0.055 3.555,1.472 3.61,3.407 z"
|
||||
id="path167"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" /></g></g><g
|
||||
id="g169"><g
|
||||
id="g171"><path
|
||||
d="m 804.125,468.439 c 0.055,1.939 -1.472,3.555 -3.409,3.61 -1.94,0.055 -3.556,-1.47 -3.611,-3.41 -0.055,-1.935 1.471,-3.552 3.411,-3.607 1.937,-0.056 3.554,1.471 3.609,3.407 z"
|
||||
id="path173"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" /></g></g></g></g><path
|
||||
d="m 761.738,405.962 c -50.342,1.438 -89.989,43.417 -88.55,93.759 1.438,50.342 43.417,89.987 93.758,88.549 50.343,-1.438 89.988,-43.415 88.55,-93.757 -1.438,-50.343 -43.415,-89.989 -93.758,-88.551 z m -4.396,163.807 c -40.192,1.148 -73.725,-31.172 -74.896,-72.19 -1.172,-41.017 30.461,-75.2 70.653,-76.348 40.191,-1.148 73.723,31.172 74.895,72.19 1.171,41.018 -30.462,75.2 -70.652,76.348 z"
|
||||
id="path179"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#f1f2f2" /><g
|
||||
id="g181" /></g></g></svg>
|
Before Width: | Height: | Size: 12 KiB |
File diff suppressed because one or more lines are too long
|
@ -1,103 +0,0 @@
|
|||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('SOGo.Common')
|
||||
.config(configure)
|
||||
|
||||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
configure.$inject = ['$mdThemingProvider'];
|
||||
function configure($mdThemingProvider) {
|
||||
|
||||
// Overwrite values to prevent flipping colors on login screen
|
||||
$mdThemingProvider.definePalette('mailcow-blue', {
|
||||
'50': 'E3F2FD',
|
||||
'100': 'BBDEFB',
|
||||
'200': '90CAF9',
|
||||
'300': '64B5F6',
|
||||
'400': '42A5F5',
|
||||
'500': '2196F3',
|
||||
'600': '1E88E5',
|
||||
'700': '1976D2',
|
||||
'800': '1565C0',
|
||||
'900': '0D47A1',
|
||||
'1000': '0D47A1',
|
||||
'A100': '82B1FF',
|
||||
'A200': '448AFF',
|
||||
'A400': '2979ff',
|
||||
'A700': '2962ff',
|
||||
'contrastDefaultColor': 'dark',
|
||||
'contrastLightColors': ['700', '800', '900'],
|
||||
'contrastDarkColors': undefined
|
||||
});
|
||||
|
||||
$mdThemingProvider.definePalette('sogo-green', {
|
||||
'50': 'E3F2FD',
|
||||
'100': 'BBDEFB',
|
||||
'200': '90CAF9',
|
||||
'300': '64B5F6',
|
||||
'400': '42A5F5',
|
||||
'500': '2196F3',
|
||||
'600': '1E88E5',
|
||||
'700': '1976D2',
|
||||
'800': '1565C0',
|
||||
'900': '0D47A1',
|
||||
'1000': '0D47A1',
|
||||
'A100': '82B1FF',
|
||||
'A200': '448AFF',
|
||||
'A400': '2979ff',
|
||||
'A700': '2962ff',
|
||||
'contrastDefaultColor': 'dark',
|
||||
'contrastLightColors': ['700', '800', '900'],
|
||||
'contrastDarkColors': undefined
|
||||
});
|
||||
|
||||
$mdThemingProvider.definePalette('default', {
|
||||
'50': 'E3F2FD',
|
||||
'100': 'BBDEFB',
|
||||
'200': '90CAF9',
|
||||
'300': '64B5F6',
|
||||
'400': '42A5F5',
|
||||
'500': '2196F3',
|
||||
'600': '1E88E5',
|
||||
'700': '1976D2',
|
||||
'800': '1565C0',
|
||||
'900': '0D47A1',
|
||||
'1000': '0D47A1',
|
||||
'A100': '82B1FF',
|
||||
'A200': '448AFF',
|
||||
'A400': '2979ff',
|
||||
'A700': '2962ff',
|
||||
'contrastDefaultColor': 'dark',
|
||||
'contrastLightColors': ['700', '800', '900'],
|
||||
'contrastDarkColors': undefined
|
||||
});
|
||||
|
||||
$mdThemingProvider.theme('default')
|
||||
.primaryPalette('mailcow-blue', {
|
||||
'default': '700', // top toolbar
|
||||
'hue-1': '500',
|
||||
'hue-2': '700', // sidebar toolbar
|
||||
'hue-3': 'A200'
|
||||
})
|
||||
.accentPalette('mailcow-blue', {
|
||||
'default': '800', // fab buttons
|
||||
'hue-1': '50', // center list toolbar
|
||||
'hue-2': '500',
|
||||
'hue-3': 'A700'
|
||||
})
|
||||
.backgroundPalette('grey', {
|
||||
'default': '50', // center list background
|
||||
'hue-1': '100',
|
||||
'hue-2': '200',
|
||||
'hue-3': '300'
|
||||
});
|
||||
|
||||
$mdThemingProvider.setDefaultTheme('default');
|
||||
$mdThemingProvider.generateThemesOnDemand(false);
|
||||
$mdThemingProvider.alwaysWatchTheme(true);
|
||||
}
|
||||
})();
|
|
@ -322,6 +322,65 @@ phpfpm_checks() {
|
|||
return 1
|
||||
}
|
||||
|
||||
ratelimit_checks() {
|
||||
err_count=0
|
||||
diff_c=0
|
||||
THRESHOLD=1
|
||||
RL_LOG_STATUS=$(redis-cli -h redis LRANGE RL_LOG 0 0 | jq .qid)
|
||||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
err_c_cur=${err_count}
|
||||
RL_LOG_STATUS_PREV=${RL_LOG_STATUS}
|
||||
RL_LOG_STATUS=$(redis-cli -h redis LRANGE RL_LOG 0 0 | jq .qid)
|
||||
if [[ ${RL_LOG_STATUS_PREV} != ${RL_LOG_STATUS} ]]; then
|
||||
err_count=$(( ${err_count} + 1 ))
|
||||
fi
|
||||
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
|
||||
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
|
||||
progress "Ratelimit" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
|
||||
if [[ $? == 10 ]]; then
|
||||
diff_c=0
|
||||
sleep 1
|
||||
else
|
||||
diff_c=0
|
||||
sleep $(( ( RANDOM % 30 ) + 10 ))
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
ipv6nat_checks() {
|
||||
err_count=0
|
||||
diff_c=0
|
||||
THRESHOLD=1
|
||||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
err_c_cur=${err_count}
|
||||
IPV6NAT_CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\")) | .id")
|
||||
if [[ ! -z ${IPV6NAT_CONTAINER_ID} ]]; then
|
||||
LATEST_STARTED="$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\") | not)" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
|
||||
LATEST_IPV6NAT="$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\"))" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
|
||||
DIFFERENCE_START_TIME=$(expr ${LATEST_IPV6NAT} - ${LATEST_STARTED} 2>/dev/null)
|
||||
if [[ "${DIFFERENCE_START_TIME}" -lt 30 ]]; then
|
||||
err_count=$(( ${err_count} + 1 ))
|
||||
fi
|
||||
fi
|
||||
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
|
||||
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
|
||||
progress "IPv6 NAT" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
|
||||
if [[ $? == 10 ]]; then
|
||||
diff_c=0
|
||||
sleep 1
|
||||
else
|
||||
diff_c=0
|
||||
sleep 3600
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
rspamd_checks() {
|
||||
err_count=0
|
||||
diff_c=0
|
||||
|
@ -448,6 +507,26 @@ done
|
|||
) &
|
||||
BACKGROUND_TASKS+=($!)
|
||||
|
||||
(
|
||||
while true; do
|
||||
if ! ratelimit_checks; then
|
||||
log_msg "Ratelimit hit error limit"
|
||||
echo ratelimit > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
) &
|
||||
BACKGROUND_TASKS+=($!)
|
||||
|
||||
(
|
||||
while true; do
|
||||
if ! ipv6nat_checks; then
|
||||
log_msg "IPv6 NAT warning: ipv6nat container was not started at least 30s after siblings (not an error)"
|
||||
echo ipv6nat > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
) &
|
||||
BACKGROUND_TASKS+=($!)
|
||||
|
||||
# Monitor watchdog agents, stop script when agents fails and wait for respawn by Docker (restart:always:n)
|
||||
(
|
||||
while true; do
|
||||
|
@ -482,7 +561,10 @@ while true; do
|
|||
CONTAINER_ID=
|
||||
HAS_INITDB=
|
||||
read com_pipe_answer </tmp/com_pipe
|
||||
if [[ ${com_pipe_answer} =~ .+-mailcow ]]; then
|
||||
if [[ ${com_pipe_answer} == "ratelimit" ]]; then
|
||||
log_msg "At least one ratelimit was applied"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}" "No further information available."
|
||||
elif [[ ${com_pipe_answer} =~ .+-mailcow ]] || [[ ${com_pipe_answer} == "ipv6nat" ]]; then
|
||||
kill -STOP ${BACKGROUND_TASKS[*]}
|
||||
sleep 3
|
||||
CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"${com_pipe_answer}\")) | .id")
|
||||
|
@ -499,9 +581,11 @@ while true; do
|
|||
else
|
||||
log_msg "Sending restart command to ${CONTAINER_ID}..."
|
||||
curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/restart
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}"
|
||||
if [[ ${com_pipe_answer} != "ipv6nat" ]]; then
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}"
|
||||
fi
|
||||
log_msg "Wait for restarted container to settle and continue watching..."
|
||||
sleep 30
|
||||
sleep 35
|
||||
fi
|
||||
fi
|
||||
kill -CONT ${BACKGROUND_TASKS[*]}
|
||||
|
|
|
@ -24,7 +24,8 @@ server {
|
|||
add_header X-Robots-Tag none;
|
||||
add_header X-Download-Options noopen;
|
||||
add_header X-Permitted-Cross-Domain-Policies none;
|
||||
add_header X-Frame-Options "SAMEORIGIN";
|
||||
#add_header X-Frame-Options "SAMEORIGIN";
|
||||
add_header Referrer-Policy "no-referrer";
|
||||
|
||||
server_name NC_SUBD;
|
||||
|
||||
|
|
|
@ -58,6 +58,11 @@ passdb {
|
|||
result_failure = continue
|
||||
result_internalfail = continue
|
||||
}
|
||||
passdb {
|
||||
driver = passwd-file
|
||||
args = /usr/local/etc/dovecot/dovecot-master.passwd
|
||||
skip = authenticated
|
||||
}
|
||||
# Set doveadm_password=your-secret-password in data/conf/dovecot/extra.conf (create if missing)
|
||||
service doveadm {
|
||||
inet_listener {
|
||||
|
@ -257,9 +262,14 @@ service lmtp {
|
|||
listen = *,[::]
|
||||
ssl_cert = </etc/ssl/mail/cert.pem
|
||||
ssl_key = </etc/ssl/mail/key.pem
|
||||
userdb {
|
||||
driver = passwd-file
|
||||
args = /usr/local/etc/dovecot/dovecot-master.userdb
|
||||
}
|
||||
userdb {
|
||||
args = /usr/local/etc/dovecot/sql/dovecot-dict-sql-userdb.conf
|
||||
driver = sql
|
||||
skip = found
|
||||
}
|
||||
protocol imap {
|
||||
imap_metadata = yes
|
||||
|
@ -347,4 +357,4 @@ auth_cache_negative_ttl = 0
|
|||
auth_cache_ttl = 30 s
|
||||
auth_cache_size = 2 M
|
||||
!include_try /usr/local/etc/dovecot/extra.conf
|
||||
default_client_limit = 10240
|
||||
default_client_limit = 10400
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
/^(.+)@mailcow.local/ OK
|
|
@ -43,7 +43,9 @@ postscreen_pipelining_enable = no
|
|||
proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_sender_acl.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_sender_dependent_default_transport_maps.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf,
|
||||
proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf,
|
||||
|
@ -126,9 +128,11 @@ mydestination = localhost.localdomain, localhost
|
|||
smtp_address_preference = ipv4
|
||||
smtp_sender_dependent_authentication = yes
|
||||
smtp_sasl_auth_enable = yes
|
||||
smtp_sasl_password_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps.cf
|
||||
smtp_sasl_password_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf
|
||||
smtp_sasl_security_options =
|
||||
smtp_sasl_mechanism_filter = plain, login
|
||||
smtp_tls_policy_maps=proxy:mysql:/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf
|
||||
smtp_header_checks = pcre:/opt/postfix/conf/anonymize_headers.pcre
|
||||
mail_name = Postcow
|
||||
transport_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf
|
||||
smtp_sasl_auth_soft_bounce = no
|
||||
|
|
|
@ -13,6 +13,7 @@ submission inet n - n - - smtpd
|
|||
588 inet n - n - - smtpd
|
||||
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
|
||||
-o smtpd_tls_auth_only=no
|
||||
-o smtpd_sender_restrictions=check_sasl_access,regexp:/opt/postfix/conf/allow_mailcow_local.regexp,reject_authenticated_sender_login_mismatch,permit_mynetworks,permit_sasl_authenticated,reject_unlisted_sender,reject_unknown_sender_domain
|
||||
590 inet n - n - - smtpd
|
||||
-o smtpd_client_restrictions=permit_mynetworks,reject
|
||||
-o smtpd_tls_auth_only=no
|
||||
|
@ -22,6 +23,8 @@ smtp_enforced_tls unix - - n - - smtp
|
|||
-o smtp_tls_security_level=encrypt
|
||||
-o syslog_name=enforced-tls-smtp
|
||||
-o smtp_delivery_status_filter=pcre:/opt/postfix/conf/smtp_dsn_filter
|
||||
smtp_via_transport_maps unix - - n - - smtp
|
||||
-o smtp_sasl_password_maps=proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf
|
||||
|
||||
tlsproxy unix - - n - 0 tlsproxy
|
||||
dnsblog unix - - n - 0 dnsblog
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
rules {
|
||||
QUARANTINE {
|
||||
backend = "http";
|
||||
url = "http://nginx:9081/pipe.php";
|
||||
selector = "is_reject";
|
||||
formatter = "default";
|
||||
meta_headers = true;
|
||||
}
|
||||
QUARANTINE {
|
||||
backend = "http";
|
||||
url = "http://nginx:9081/pipe.php";
|
||||
selector = "is_reject";
|
||||
formatter = "default";
|
||||
meta_headers = true;
|
||||
}
|
||||
RLINFO {
|
||||
backend = "http";
|
||||
url = "http://nginx:9081/pipe_rl.php";
|
||||
selector = "ratelimited";
|
||||
formatter = "json";
|
||||
}
|
||||
}
|
||||
custom_select {
|
||||
ratelimited = <<EOD
|
||||
return function(task)
|
||||
local ratelimited = task:get_symbol("RATELIMITED")
|
||||
if ratelimited then
|
||||
return true
|
||||
end
|
||||
return
|
||||
end
|
||||
EOD;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
// File size is limited by Nginx site to 10M
|
||||
// To speed things up, we do not include prerequisites
|
||||
header('Content-Type: text/plain');
|
||||
require_once "vars.inc.php";
|
||||
// Do not show errors, we log to using error_log
|
||||
ini_set('error_reporting', 0);
|
||||
// Init Redis
|
||||
$redis = new Redis();
|
||||
$redis->connect('redis-mailcow', 6379);
|
||||
|
||||
$raw_data_content = file_get_contents('php://input');
|
||||
$raw_data_decoded = json_decode($raw_data_content, true);
|
||||
|
||||
$data['time'] = time();
|
||||
$data['rcpt'] = implode(', ', $raw_data_decoded['rcpt']);
|
||||
$data['from'] = $raw_data_decoded['from'];
|
||||
$data['user'] = $raw_data_decoded['user'];
|
||||
$symbol_rl_key = array_search('RATELIMITED', array_column($raw_data_decoded['symbols'], 'name'));
|
||||
$data['rl_info'] = implode($raw_data_decoded['symbols'][$symbol_rl_key]['options']);
|
||||
preg_match('/(.+)\((.+)\)/i', $data['rl_info'], $rl_matches);
|
||||
if (!empty($rl_matches[1]) && !empty($rl_matches[2])) {
|
||||
$data['rl_name'] = $rl_matches[1];
|
||||
$data['rl_hash'] = $rl_matches[2];
|
||||
}
|
||||
else {
|
||||
$data['rl_name'] = 'err';
|
||||
$data['rl_hash'] = 'err';
|
||||
}
|
||||
$data['qid'] = $raw_data_decoded['qid'];
|
||||
$data['ip'] = $raw_data_decoded['ip'];
|
||||
$data['message_id'] = $raw_data_decoded['message_id'];
|
||||
$data['header_subject'] = implode(' ', $raw_data_decoded['header_subject']);
|
||||
$data['header_from'] = implode(', ', $raw_data_decoded['header_from']);
|
||||
|
||||
$redis->lpush('RL_LOG', json_encode($data));
|
||||
exit;
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
rates {
|
||||
# Format: "1 / 1h" or "20 / 1m" etc. - global ratelimits are disabled by default
|
||||
to = "100 / 1s";
|
||||
to_ip = "100 / 1s";
|
||||
to_ip_from = "100 / 1s";
|
||||
to = "45 / 1m";
|
||||
to_ip = "360 / 1m";
|
||||
to_ip_from = "180 / 1m";
|
||||
bounce_to = "100 / 1s";
|
||||
bounce_to_ip = "100 / 1s";
|
||||
}
|
||||
whitelisted_rcpts = "postmaster,mailer-daemon";
|
||||
max_rcpt = 5;
|
||||
custom_keywords = "/etc/rspamd/lua/ratelimit.lua";
|
||||
info_symbol = "RATELIMITED";
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY st0 "fill:#50BD37;">
|
||||
]>
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="640px" height="350px" viewBox="78.712 58.488 640 350" style="enable-background:new 78.712 58.488 640 350;"
|
||||
xml:space="preserve">
|
||||
<path style="&st0;" d="M648.541,145.679c-9.947,0-17.009-7.278-17.009-17.048c0-9.777,7.062-17.057,17.009-17.057
|
||||
c10.024,0,17.086,7.279,17.086,17.057C665.627,138.401,658.565,145.679,648.541,145.679z M648.511,94.893
|
||||
c-19.693,0-33.679,14.4-33.679,33.738c0,19.33,13.985,33.729,33.679,33.729c19.822,0,33.808-14.4,33.808-33.729
|
||||
C682.318,109.293,668.333,94.893,648.511,94.893z M648.482,179.843c-29.889,0-51.123-21.868-51.123-51.212
|
||||
c0-29.353,21.234-51.209,51.123-51.209c30.082,0,51.307,21.856,51.307,51.209C699.789,157.975,678.564,179.843,648.482,179.843z
|
||||
M648.442,58.488c-40.929,0-69.995,29.946-69.995,70.143c0,40.189,29.066,70.125,69.995,70.125c41.194,0,70.27-29.937,70.27-70.125
|
||||
C718.712,88.434,689.637,58.488,648.442,58.488z M158.166,183.902l-21.018-5.008c-19.131-4.396-28.849-9.413-28.849-23.21
|
||||
c0-15.684,15.99-21.965,30.419-21.965c14.667,0,25.382,7.329,31.693,18.737c0.02,0.048,0.051,0.097,0.09,0.157
|
||||
c0.127,0.247,0.276,0.484,0.403,0.731l0.03-0.02c1.985,3.002,5.323,5.008,8.919,5.008c6.122,0,10.558-4.425,10.558-10.547
|
||||
c0-2.341-0.504-4.82-1.601-6.688c-10.764-18.302-28.513-26.192-48.838-26.192c-27.594,0-54.262,13.797-54.262,44.218
|
||||
c0,27.921,27.605,36.079,37.64,38.578l20.069,4.71c15.368,3.763,27.912,8.791,27.912,23.517c0,16.938-17.561,23.943-34.499,23.943
|
||||
c-17.245,0-30.015-9.37-38.814-22.37h-0.01c-1.956-3-4.988-4.328-8.702-4.328c-5.984,0-10.805,5.185-10.587,11.162
|
||||
c0.098,2.438,0.909,4.637,2.153,6.405c13.787,20.633,33.728,28.41,55.96,28.41c28.543,0,57.085-13.143,57.085-45.132
|
||||
C193.918,203.325,178.551,188.613,158.166,183.902z M298.479,250.312c-33.866,0-55.199-25.403-55.199-58.331
|
||||
c0-32.939,21.333-58.343,55.199-58.343c34.192,0,55.516,25.403,55.516,58.343C353.996,224.91,332.672,250.312,298.479,250.312z
|
||||
M298.479,114.823c-45.471,0-77.777,32.93-77.777,77.158c0,44.217,32.306,77.146,77.777,77.146
|
||||
c45.786,0,78.093-32.929,78.093-77.146C376.572,147.753,344.266,114.823,298.479,114.823z M518.715,234.312
|
||||
c-0.771,0.74-1.549,1.472-2.399,2.175c-1.106,1.014-2.391,2.112-3.854,3.208c-8.829,6.391-19.979,10.094-33.017,10.094
|
||||
c-33.876,0-55.198-25.402-55.198-58.332c0-32.939,21.322-58.342,55.198-58.342c34.183,0,55.506,25.403,55.506,58.342
|
||||
C534.951,208.653,529.135,223.774,518.715,234.312z M468.097,317.938c2.528,0,5.146-0.168,7.863-0.504
|
||||
c5.018-0.631,9.588-0.909,13.729-0.909c19.24,0.109,29.036,5.7,34.943,12.158c5.895,6.499,8.168,15.311,8.158,22.796
|
||||
c0.01,3.586-0.555,6.795-1.177,8.721c-2.944,8.93-8.888,15.002-17.996,19.576c-9.035,4.484-21.095,6.777-33.707,6.757
|
||||
c-4.514,0-9.105-0.288-13.639-0.831c-8.573-0.987-19.911-4.671-28.13-11.093c-4.138-3.199-6.458-6.991-8.858-11.485
|
||||
c-2.379-4.514-2.783-9.748-2.783-16.442v-0.742c0-12.346,4.84-20.544,11.051-26.5c3.07-2.904,5.69-5.064,7.99-6.438
|
||||
c0.366-0.218,0.438-0.416,0.755-0.593C452.39,316.014,459.684,317.968,468.097,317.938z M479.445,114.301
|
||||
c-45.471,0-77.786,32.929-77.786,77.157c0,29.887,14.765,54.598,38.378,67.489c-0.314,0.314-0.621,0.641-0.916,0.966
|
||||
c-6.104,6.687-9.226,15.25-9.236,23.913c-0.008,3.821,0.624,7.741,1.977,11.494c-3.062,1.956-6.717,4.634-10.46,8.147
|
||||
c-9.026,8.408-18.734,22.541-19.021,42.097c-0.01,0.454-0.01,0.829-0.01,1.118c-0.01,10.071,2.379,19.157,6.459,26.774
|
||||
c6.133,11.466,15.683,19.445,25.539,24.77c9.917,5.334,20.257,8.166,29.273,9.274c5.373,0.643,10.826,0.988,16.268,0.988
|
||||
c15.151-0.02,30.261-2.578,43.409-9.019c13.085-6.34,24.333-17.253,29.192-32.562c1.443-4.553,2.212-9.719,2.231-15.428
|
||||
c-0.02-11.595-3.349-25.759-13.767-37.452c-10.421-11.734-27.654-19.566-51.288-19.459c-5.138,0-10.606,0.356-16.426,1.078
|
||||
c-1.877,0.227-3.596,0.334-5.166,0.334c-7.239-0.048-10.872-2.053-13.036-4.098c-2.133-2.084-3.2-4.839-3.229-8.058
|
||||
c-0.01-3.28,1.284-6.727,3.467-9.078c2.231-2.332,5.008-3.91,9.846-3.97c0.436,0,0.9,0.01,1.374,0.05
|
||||
c3.101,0.216,6.112,0.325,9.037,0.325c24.188,0.047,42.38-7.448,54.756-17.759c12.415-10.312,18.971-22.854,22.071-32.76l-0.04-0.01
|
||||
c3.37-8.899,5.197-18.715,5.197-29.166C557.539,147.229,525.234,114.301,479.445,114.301z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 4.4 KiB |
|
@ -11,10 +11,10 @@
|
|||
SOGoDraftsFolderName = "Drafts";
|
||||
SOGoJunkFolderName= "Junk";
|
||||
SOGoMailDomain = "sogo.local";
|
||||
SOGoEnableEMailAlarms = NO;
|
||||
SOGoEnableEMailAlarms = YES;
|
||||
SOGoFoldersSendEMailNotifications = YES;
|
||||
SOGoForwardEnabled = YES;
|
||||
SOGoUIAdditionalJSFiles = (js/theme-blue.js, js/custom-sogo.js);
|
||||
SOGoUIAdditionalJSFiles = (js/custom-sogo.js);
|
||||
|
||||
// Multi-domain setup
|
||||
// Domains are isolated, you can define visibility options here.
|
||||
|
|
|
@ -10,6 +10,7 @@ $tfa_data = get_tfa();
|
|||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#tab-access" aria-controls="tab-access" role="tab" data-toggle="tab"><?=$lang['admin']['access'];?></a></li>
|
||||
<li role="presentation"><a href="#tab-config" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['configuration'];?></a></li>
|
||||
<li role="presentation"><a href="#tab-routing" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['routing'];?></a></li>
|
||||
<li role="presentation"><a href="#tab-sys-mails" aria-controls="tab-sys-mails" role="tab" data-toggle="tab"><?=$lang['admin']['sys_mails'];?></a></li>
|
||||
<li role="presentation"><a href="#tab-mailq" aria-controls="tab-mailq" role="tab" data-toggle="tab"><?=$lang['admin']['queue_manager'];?></a></li>
|
||||
</ul>
|
||||
|
@ -178,6 +179,101 @@ $tfa_data = get_tfa();
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-routing">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><?=$lang['admin']['relayhosts'];?></div>
|
||||
<div class="panel-body">
|
||||
<p style="margin-bottom:40px"><?=$lang['admin']['relayhosts_hint'];?></p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-condensed" id="relayhoststable"></table>
|
||||
</div>
|
||||
<div class="mass-actions-admin">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" id="toggle_multi_select_all" data-id="rlyhosts" class="btn btn-default"><?=$lang['mailbox']['toggle_all'];?></button>
|
||||
<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 data-action="edit_selected" data-id="rlyhosts" data-api-url='edit/relayhost' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
|
||||
<li><a data-action="edit_selected" data-id="rlyhosts" data-api-url='edit/relayhost' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a data-action="delete_selected" data-id="rlyhosts" data-api-url='delete/relayhost' href="#"><?=$lang['admin']['remove'];?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<legend><?=$lang['admin']['add_relayhost'];?></legend>
|
||||
<p class="help-block"><?=$lang['admin']['add_relayhost_hint'];?></p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form class="form" data-id="rlyhost" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label for="hostname"><?=$lang['admin']['host'];?></label>
|
||||
<input class="form-control input-sm" name="hostname" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="username"><?=$lang['admin']['username'];?></label>
|
||||
<input class="form-control input-sm" name="username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password"><?=$lang['admin']['password'];?></label>
|
||||
<input class="form-control input-sm" name="password">
|
||||
</div>
|
||||
<button class="btn btn-default" data-action="add_item" data-id="rlyhost" data-api-url='add/relayhost' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><?=$lang['admin']['transport_maps'];?></div>
|
||||
<div class="panel-body">
|
||||
<p style="margin-bottom:40px"><?=$lang['admin']['transports_hint'];?></p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-condensed" id="transportstable"></table>
|
||||
</div>
|
||||
<div class="mass-actions-admin">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" id="toggle_multi_select_all" data-id="transports" class="btn btn-default"><?=$lang['mailbox']['toggle_all'];?></button>
|
||||
<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 data-action="edit_selected" data-id="transports" data-api-url='edit/transport' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
|
||||
<li><a data-action="edit_selected" data-id="transports" data-api-url='edit/transport' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a data-action="delete_selected" data-id="transports" data-api-url='delete/transport' href="#"><?=$lang['admin']['remove'];?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<legend><?=$lang['admin']['add_transport'];?></legend>
|
||||
<p class="help-block"><?=$lang['admin']['add_transports_hint'];?></p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form class="form" data-id="transport" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label for="destination"><?=$lang['admin']['destination'];?></label>
|
||||
<input class="form-control input-sm" name="destination" placeholder='example.org, .example.org, *, box@example.org' required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="nexthop"><?=$lang['admin']['nexthop'];?></label>
|
||||
<input class="form-control input-sm" name="nexthop" placeholder='host:25, host, [host]:25, [0.0.0.0]:25' required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="username"><?=$lang['admin']['username'];?></label>
|
||||
<input class="form-control input-sm" name="username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password"><?=$lang['admin']['password'];?></label>
|
||||
<input class="form-control" name="password">
|
||||
</div>
|
||||
<p class="help-block"><?=$lang['admin']['credentials_transport_warning'];?></p>
|
||||
<button class="btn btn-default" data-action="add_item" data-id="transport" data-api-url='add/transport' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-config">
|
||||
<div class="row">
|
||||
<div id="sidebar-admin" class="col-sm-2 hidden-xs">
|
||||
|
@ -185,7 +281,6 @@ $tfa_data = get_tfa();
|
|||
<a href="#dkim" class="list-group-item"><?=$lang['admin']['dkim_keys'];?></a>
|
||||
<a href="#fwdhosts" class="list-group-item"><?=$lang['admin']['forwarding_hosts'];?></a>
|
||||
<a href="#f2bparams" class="list-group-item"><?=$lang['admin']['f2b_parameters'];?></a>
|
||||
<a href="#relayhosts" class="list-group-item">Relayhosts</a>
|
||||
<a href="#quarantine" class="list-group-item"><?=$lang['admin']['quarantine'];?></a>
|
||||
<a href="#rsettings" class="list-group-item">Rspamd settings map</a>
|
||||
<a href="#customize" class="list-group-item"><?=$lang['admin']['customize'];?></a>
|
||||
|
@ -333,7 +428,7 @@ $tfa_data = get_tfa();
|
|||
<input class="form-control input-sm" name="dkim_selector" value="dkim" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="private_key_file"><?=$lang['admin']['private_key'];?>:</label>
|
||||
<label for="private_key_file"><?=$lang['admin']['private_key'];?>: (RSA PKCS#8)</label>
|
||||
<textarea class="form-control input-sm" rows="10" name="private_key_file" id="private_key_file" required placeholder="-----BEGIN RSA KEY-----"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-default" data-action="add_item" data-id="dkim_import" data-api-url='add/dkim_import' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['import'];?></button>
|
||||
|
@ -513,46 +608,6 @@ $tfa_data = get_tfa();
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<span class="anchor" id="relayhosts"></span>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Relayhosts</div>
|
||||
<div class="panel-body">
|
||||
<p style="margin-bottom:40px"><?=$lang['admin']['relayhosts_hint'];?></p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-condensed" id="relayhoststable"></table>
|
||||
</div>
|
||||
<div class="mass-actions-admin">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" id="toggle_multi_select_all" data-id="rlyhosts" class="btn btn-default"><?=$lang['mailbox']['toggle_all'];?></button>
|
||||
<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 data-action="edit_selected" data-id="rlyhosts" data-api-url='edit/relayhost' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
|
||||
<li><a data-action="edit_selected" data-id="rlyhosts" data-api-url='edit/relayhost' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a data-action="delete_selected" data-id="rlyhosts" data-api-url='delete/relayhost' href="#"><?=$lang['admin']['remove'];?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<legend><?=$lang['admin']['add_relayhost'];?></legend>
|
||||
<p class="help-block"><?=$lang['admin']['add_relayhost_add_hint'];?></p>
|
||||
<form class="form" data-id="rlyhost" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label for="hostname"><?=$lang['admin']['host'];?></label>
|
||||
<input class="form-control" name="hostname" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hostname"><?=$lang['admin']['username'];?></label>
|
||||
<input class="form-control" name="username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hostname"><?=$lang['admin']['password'];?></label>
|
||||
<input class="form-control" name="password">
|
||||
</div>
|
||||
<button class="btn btn-default" data-action="add_item" data-id="rlyhost" data-api-url='add/relayhost' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="anchor" id="quarantine"></span>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><?=$lang['admin']['quarantine'];?></div>
|
||||
|
|
|
@ -9,7 +9,7 @@ table.footable>tbody>tr.footable-empty>td {
|
|||
overflow: visible !important;
|
||||
}
|
||||
.table-responsive {
|
||||
overflow: visible !important;
|
||||
overflow: auto !important;
|
||||
}
|
||||
@media screen and (max-width: 767px) {
|
||||
.table-responsive {
|
||||
|
|
|
@ -22,6 +22,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
<li role="presentation"><a href="#tab-watchdog-logs" aria-controls="tab-watchdog-logs" role="tab" data-toggle="tab">Watchdog</a></li>
|
||||
<li role="presentation"><a href="#tab-acme-logs" aria-controls="tab-acme-logs" role="tab" data-toggle="tab">ACME</a></li>
|
||||
<li role="presentation"><a href="#tab-api-logs" aria-controls="tab-api-logs" role="tab" data-toggle="tab">API</a></li>
|
||||
<li role="presentation"><a href="#tab-api-rl" aria-controls="tab-api-rl" role="tab" data-toggle="tab">Ratelimits</a></li>
|
||||
<li role="presentation"><span class="dropdown-desc"><?=$lang['debug']['external_logs'];?></span></li>
|
||||
<li role="presentation"><a href="#tab-rspamd-history" aria-controls="tab-rspamd-history" role="tab" data-toggle="tab">Rspamd</a></li>
|
||||
<li role="presentation"><span class="dropdown-desc"><?=$lang['debug']['static_logs'];?></span></li>
|
||||
|
@ -272,6 +273,24 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-api-rl">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Ratelimits <span class="badge badge-info table-lines"></span>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rllog" data-table="rl_log" data-log-url="ratelimited" data-nrows="100">+ 100</button>
|
||||
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rllog" data-table="rl_log" data-log-url="ratelimited" data-nrows="1000">+ 1000</button>
|
||||
<button class="btn btn-xs btn-default refresh_table" data-draw="draw_rl_logs" data-table="rl_log"><?=$lang['admin']['refresh'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="help-block"><?=$lang['admin']['hash_remove_info'];?></p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-condensed" id="rl_log"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /tab-content -->
|
||||
</div> <!-- /col-md-12 -->
|
||||
</div> <!-- /row -->
|
||||
|
|
|
@ -271,7 +271,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="quota">Relayhost</label>
|
||||
<label class="control-label col-sm-2" for="quota"><?=$lang['edit']['relayhost'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<select data-live-search="true" name="relayhost" class="form-control">
|
||||
<?php
|
||||
|
@ -330,7 +330,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
<hr>
|
||||
<form data-id="domratelimit" class="form-inline well" method="post">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Ratelimit</label>
|
||||
<label class="control-label"><?=$lang['acl']['ratelimit'];?></label>
|
||||
<input name="rl_value" type="number" value="<?=(!empty($rl['value'])) ? $rl['value'] : null;?>" autocomplete="off" class="form-control" placeholder="disabled">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -440,7 +440,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
<hr>
|
||||
<form data-id="domratelimit" class="form-inline well" method="post">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Ratelimit</label>
|
||||
<label class="control-label"><?=$lang['acl']['ratelimit'];?></label>
|
||||
<input name="rl_value" type="number" value="<?=(!empty($rl['value'])) ? $rl['value'] : null;?>" autocomplete="off" class="form-control" placeholder="disabled">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -600,7 +600,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
<form data-id="mboxratelimit" class="form-inline well" method="post">
|
||||
<div class="row">
|
||||
<div class="col-sm-1">
|
||||
<p class="help-block">Ratelimit</p>
|
||||
<p class="help-block"><?=$lang['acl']['ratelimit'];?></p>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<div class="form-group">
|
||||
|
@ -693,6 +693,59 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||
<?php
|
||||
}
|
||||
}
|
||||
elseif (isset($_GET['transport']) && is_numeric($_GET["transport"]) && !empty($_GET["transport"])) {
|
||||
$transport = intval($_GET["transport"]);
|
||||
$result = transport('details', $transport);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<h4><?=$lang['edit']['resource'];?></h4>
|
||||
<form class="form-horizontal" role="form" method="post" data-id="edittransport">
|
||||
<input type="hidden" value="0" name="active">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="destination"><?=$lang['add']['destination'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="destination" value="<?=htmlspecialchars($result['destination'], ENT_QUOTES, 'UTF-8');?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="nexthop"><?=$lang['edit']['nexthop'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="nexthop" value="<?=htmlspecialchars($result['nexthop'], ENT_QUOTES, 'UTF-8');?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="username" value="<?=htmlspecialchars($result['username'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password"><?=$lang['add']['password'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" data-hibp="true" class="form-control" name="password" value="<?=htmlspecialchars($result['password'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</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" : null;?>> <?=$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" data-action="edit_selected" data-id="edittransport" data-item="<?=htmlspecialchars($result['id']);?>" data-api-url='edit/transport' data-api-attr='{}' href="#"><?=$lang['edit']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
elseif (isset($_GET['resource']) && filter_var(html_entity_decode(rawurldecode($_GET["resource"])), FILTER_VALIDATE_EMAIL) && !empty($_GET["resource"])) {
|
||||
$resource = html_entity_decode(rawurldecode($_GET["resource"]));
|
||||
$result = mailbox('get', 'resource_details', $resource);
|
||||
|
|
|
@ -4,23 +4,49 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.inc.php';
|
|||
|
||||
error_reporting(0);
|
||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
||||
$relayhost_id = intval($_GET['relayhost_id']);
|
||||
$transport_id = intval($_GET['transport_id']);
|
||||
$transport_type = $_GET['transport_type'];
|
||||
if (isset($_GET['mail_from']) && filter_var($_GET['mail_from'], FILTER_VALIDATE_EMAIL)) {
|
||||
$mail_from = $_GET['mail_from'];
|
||||
}
|
||||
else {
|
||||
$mail_from = "relay@example.org";
|
||||
}
|
||||
$relayhost_details = relayhost('details', $relayhost_id);
|
||||
if (!empty($relayhost_details)) {
|
||||
if ($transport_type == 'transport-map') {
|
||||
$transport_details = transport('details', $transport_id);
|
||||
$nexthop = $transport_details['nexthop'];
|
||||
}
|
||||
elseif ($transport_type == 'sender-dependent') {
|
||||
$transport_details = relayhost('details', $transport_id);
|
||||
$nexthop = $transport_details['hostname'];
|
||||
}
|
||||
if (!empty($transport_details)) {
|
||||
// Remove [ and ]
|
||||
$hostname_w_port = preg_replace('/\[|\]/', '', $relayhost_details['hostname']);
|
||||
$hostname_w_port = preg_replace('/\[|\]/', '', $nexthop);
|
||||
$skip_lookup_mx = strpos($nexthop, '[');
|
||||
// Explode to hostname and port
|
||||
list($hostname, $port) = explode(':', $hostname_w_port);
|
||||
// Try to get MX if host is not [host]
|
||||
if ($skip_lookup_mx === false) {
|
||||
getmxrr($hostname, $mx_records, $mx_weight);
|
||||
if (!empty($mx_records)) {
|
||||
for ($i = 0; $i < count($mx_records); $i++) {
|
||||
$mxs[$mx_records[$i]] = $mx_weight[$i];
|
||||
}
|
||||
asort ($mxs);
|
||||
$records = array_keys($mxs);
|
||||
echo 'Using first matched primary MX for "' . $hostname . '": ';
|
||||
$hostname = $records[0];
|
||||
echo $hostname . '<br>';
|
||||
}
|
||||
else {
|
||||
echo 'No MX records for ' . $hostname . ' were found in DNS, skipping and using hostname as next-hop.<br>';
|
||||
}
|
||||
}
|
||||
// Use port 25 if no port was given
|
||||
$port = (empty($port)) ? 25 : $port;
|
||||
$username = $relayhost_details['username'];
|
||||
$password = $relayhost_details['password'];
|
||||
$username = $transport_details['username'];
|
||||
$password = $transport_details['password'];
|
||||
|
||||
$mail = new PHPMailer;
|
||||
$mail->Timeout = 10;
|
||||
|
@ -73,7 +99,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
|
|||
$mail->send();
|
||||
}
|
||||
else {
|
||||
echo "Unknown relayhost.";
|
||||
echo "Unknown transport.";
|
||||
}
|
||||
}
|
||||
else {
|
|
@ -123,7 +123,7 @@ function dkim($_action, $_data = null) {
|
|||
try {
|
||||
$redis->hSet('DKIM_PUB_KEYS', $to_domain, $from_domain_dkim['pubkey']);
|
||||
$redis->hSet('DKIM_SELECTORS', $to_domain, $from_domain_dkim['dkim_selector']);
|
||||
$redis->hSet('DKIM_PRIV_KEYS', $from_domain_dkim['dkim_selector'] . '.' . $to_domain, trim($from_domain_dkim['privkey']));
|
||||
$redis->hSet('DKIM_PRIV_KEYS', $from_domain_dkim['dkim_selector'] . '.' . $to_domain, base64_decode(trim($from_domain_dkim['privkey'])));
|
||||
}
|
||||
catch (RedisException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
|
|
|
@ -5,6 +5,9 @@ function domain_admin($_action, $_data = null) {
|
|||
$_data_log = $_data;
|
||||
!isset($_data_log['password']) ?: $_data_log['password'] = '*';
|
||||
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
|
||||
!isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
|
||||
!isset($_data_log['user_new_pass']) ?: $_data_log['user_new_pass'] = '*';
|
||||
!isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*';
|
||||
switch ($_action) {
|
||||
case 'add':
|
||||
$username = strtolower(trim($_data['username']));
|
||||
|
|
|
@ -1263,7 +1263,7 @@ function get_u2f_registrations($username) {
|
|||
$sel->execute(array($username));
|
||||
return $sel->fetchAll(PDO::FETCH_OBJ);
|
||||
}
|
||||
function get_logs($container, $lines = false) {
|
||||
function get_logs($application, $lines = false) {
|
||||
if ($lines === false) {
|
||||
$lines = $GLOBALS['LOG_LINES'] - 1;
|
||||
}
|
||||
|
@ -1283,7 +1283,7 @@ function get_logs($container, $lines = false) {
|
|||
return false;
|
||||
}
|
||||
// SQL
|
||||
if ($container == "mailcow-ui") {
|
||||
if ($application == "mailcow-ui") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$stmt = $pdo->prepare("SELECT * FROM `logs` ORDER BY `id` DESC LIMIT :from, :to");
|
||||
$stmt->execute(array(
|
||||
|
@ -1304,7 +1304,7 @@ function get_logs($container, $lines = false) {
|
|||
}
|
||||
}
|
||||
// Redis
|
||||
if ($container == "dovecot-mailcow") {
|
||||
if ($application == "dovecot-mailcow") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$data = $redis->lRange('DOVECOT_MAILLOG', $from - 1, $to - 1);
|
||||
}
|
||||
|
@ -1318,7 +1318,7 @@ function get_logs($container, $lines = false) {
|
|||
return $data_array;
|
||||
}
|
||||
}
|
||||
if ($container == "postfix-mailcow") {
|
||||
if ($application == "postfix-mailcow") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$data = $redis->lRange('POSTFIX_MAILLOG', $from - 1, $to - 1);
|
||||
}
|
||||
|
@ -1332,7 +1332,7 @@ function get_logs($container, $lines = false) {
|
|||
return $data_array;
|
||||
}
|
||||
}
|
||||
if ($container == "sogo-mailcow") {
|
||||
if ($application == "sogo-mailcow") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$data = $redis->lRange('SOGO_LOG', $from - 1, $to - 1);
|
||||
}
|
||||
|
@ -1346,7 +1346,7 @@ function get_logs($container, $lines = false) {
|
|||
return $data_array;
|
||||
}
|
||||
}
|
||||
if ($container == "watchdog-mailcow") {
|
||||
if ($application == "watchdog-mailcow") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$data = $redis->lRange('WATCHDOG_LOG', $from - 1, $to - 1);
|
||||
}
|
||||
|
@ -1360,7 +1360,7 @@ function get_logs($container, $lines = false) {
|
|||
return $data_array;
|
||||
}
|
||||
}
|
||||
if ($container == "acme-mailcow") {
|
||||
if ($application == "acme-mailcow") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$data = $redis->lRange('ACME_LOG', $from - 1, $to - 1);
|
||||
}
|
||||
|
@ -1374,7 +1374,21 @@ function get_logs($container, $lines = false) {
|
|||
return $data_array;
|
||||
}
|
||||
}
|
||||
if ($container == "api-mailcow") {
|
||||
if ($application == "ratelimited") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$data = $redis->lRange('RL_LOG', $from - 1, $to - 1);
|
||||
}
|
||||
else {
|
||||
$data = $redis->lRange('RL_LOG', 0, $lines);
|
||||
}
|
||||
if ($data) {
|
||||
foreach ($data as $json_line) {
|
||||
$data_array[] = json_decode($json_line, true);
|
||||
}
|
||||
return $data_array;
|
||||
}
|
||||
}
|
||||
if ($application == "api-mailcow") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$data = $redis->lRange('API_LOG', $from - 1, $to - 1);
|
||||
}
|
||||
|
@ -1388,7 +1402,7 @@ function get_logs($container, $lines = false) {
|
|||
return $data_array;
|
||||
}
|
||||
}
|
||||
if ($container == "netfilter-mailcow") {
|
||||
if ($application == "netfilter-mailcow") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$data = $redis->lRange('NETFILTER_LOG', $from - 1, $to - 1);
|
||||
}
|
||||
|
@ -1402,7 +1416,7 @@ function get_logs($container, $lines = false) {
|
|||
return $data_array;
|
||||
}
|
||||
}
|
||||
if ($container == "autodiscover-mailcow") {
|
||||
if ($application == "autodiscover-mailcow") {
|
||||
if (isset($from) && isset($to)) {
|
||||
$data = $redis->lRange('AUTODISCOVER_LOG', $from - 1, $to - 1);
|
||||
}
|
||||
|
@ -1416,7 +1430,7 @@ function get_logs($container, $lines = false) {
|
|||
return $data_array;
|
||||
}
|
||||
}
|
||||
if ($container == "rspamd-history") {
|
||||
if ($application == "rspamd-history") {
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_UNIX_SOCKET_PATH, '/var/lib/rspamd/rspamd.sock');
|
||||
if (!is_numeric($lines)) {
|
||||
|
|
|
@ -192,5 +192,44 @@ function ratelimit($_action, $_scope, $_data = null) {
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
$data['hash'] = $_data;
|
||||
if ($_SESSION['mailcow_cc_role'] != 'admin' || !preg_match('/^RL[0-9A-Za-z=]+$/i', trim($data['hash']))) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if ($redis->exists($data['hash'])) {
|
||||
$redis->delete($data['hash']);
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
|
||||
'msg' => 'hash_deleted'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
|
||||
'msg' => 'hash_not_found'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (RedisException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
|
||||
'msg' => array('redis_error', $e)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
<?php
|
||||
function relayhost($_action, $_data = null) {
|
||||
global $pdo;
|
||||
global $lang;
|
||||
$_data_log = $_data;
|
||||
switch ($_action) {
|
||||
case 'add':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$hostname = trim($_data['hostname']);
|
||||
$username = str_replace(':', '\:', trim($_data['username']));
|
||||
$password = str_replace(':', '\:', trim($_data['password']));
|
||||
if (empty($hostname)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('invalid_host', htmlspecialchars($host))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("INSERT INTO `relayhosts` (`hostname`, `username` ,`password`, `active`)
|
||||
VALUES (:hostname, :username, :password, :active)");
|
||||
$stmt->execute(array(
|
||||
':hostname' => $hostname,
|
||||
':username' => $username,
|
||||
':password' => str_replace(':', '\:', $password),
|
||||
':active' => '1'
|
||||
));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('relayhost_added', htmlspecialchars(implode(', ', $hosts)))
|
||||
);
|
||||
break;
|
||||
case 'edit':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$ids = (array)$_data['id'];
|
||||
foreach ($ids as $id) {
|
||||
$is_now = relayhost('details', $id);
|
||||
if (!empty($is_now)) {
|
||||
$hostname = (!empty($_data['hostname'])) ? trim($_data['hostname']) : $is_now['hostname'];
|
||||
$username = (isset($_data['username'])) ? trim($_data['username']) : $is_now['username'];
|
||||
$password = (isset($_data['password'])) ? trim($_data['password']) : $is_now['password'];
|
||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('relayhost_invalid', $id)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE `relayhosts` SET
|
||||
`hostname` = :hostname,
|
||||
`username` = :username,
|
||||
`password` = :password,
|
||||
`active` = :active
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(
|
||||
':id' => $id,
|
||||
':hostname' => $hostname,
|
||||
':username' => $username,
|
||||
':password' => $password,
|
||||
':active' => $active
|
||||
));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('object_modified', htmlspecialchars(implode(', ', $hostnames)))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$ids = (array)$_data['id'];
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
$stmt = $pdo->prepare("DELETE FROM `relayhosts` WHERE `id`= :id");
|
||||
$stmt->execute(array(':id' => $id));
|
||||
$stmt = $pdo->prepare("UPDATE `domain` SET `relayhost` = '0' WHERE `relayhost`= :id");
|
||||
$stmt->execute(array(':id' => $id));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('relayhost_removed', htmlspecialchars($id))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'get':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
return false;
|
||||
}
|
||||
$relayhosts = array();
|
||||
$stmt = $pdo->query("SELECT `id`, `hostname`, `username` FROM `relayhosts`");
|
||||
$relayhosts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $relayhosts;
|
||||
break;
|
||||
case 'details':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
|
||||
return false;
|
||||
}
|
||||
$relayhostdata = array();
|
||||
$stmt = $pdo->prepare("SELECT `id`,
|
||||
`hostname`,
|
||||
`username`,
|
||||
`password`,
|
||||
`active` AS `active_int`,
|
||||
CONCAT(LEFT(`password`, 3), '...') AS `password_short`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM `relayhosts`
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(':id' => $_data));
|
||||
$relayhostdata = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!empty($relayhostdata)) {
|
||||
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(`domain` SEPARATOR ', ') AS `used_by_domains` FROM `domain` WHERE `relayhost` = :id");
|
||||
$stmt->execute(array(':id' => $_data));
|
||||
$used_by_domains = $stmt->fetch(PDO::FETCH_ASSOC)['used_by_domains'];
|
||||
$used_by_domains = (empty($used_by_domains)) ? '' : $used_by_domains;
|
||||
$relayhostdata['used_by_domains'] = $used_by_domains;
|
||||
}
|
||||
return $relayhostdata;
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,449 @@
|
|||
<?php
|
||||
function relayhost($_action, $_data = null) {
|
||||
global $pdo;
|
||||
global $lang;
|
||||
$_data_log = $_data;
|
||||
switch ($_action) {
|
||||
case 'add':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$hostname = trim($_data['hostname']);
|
||||
$username = str_replace(':', '\:', trim($_data['username']));
|
||||
$password = str_replace(':', '\:', trim($_data['password']));
|
||||
if (empty($hostname)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('invalid_host', htmlspecialchars($host))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("INSERT INTO `relayhosts` (`hostname`, `username` ,`password`, `active`)
|
||||
VALUES (:hostname, :username, :password, :active)");
|
||||
$stmt->execute(array(
|
||||
':hostname' => $hostname,
|
||||
':username' => $username,
|
||||
':password' => str_replace(':', '\:', $password),
|
||||
':active' => '1'
|
||||
));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('relayhost_added', htmlspecialchars(implode(', ', $hosts)))
|
||||
);
|
||||
break;
|
||||
case 'edit':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$ids = (array)$_data['id'];
|
||||
foreach ($ids as $id) {
|
||||
$is_now = relayhost('details', $id);
|
||||
if (!empty($is_now)) {
|
||||
$hostname = (!empty($_data['hostname'])) ? trim($_data['hostname']) : $is_now['hostname'];
|
||||
$username = (isset($_data['username'])) ? trim($_data['username']) : $is_now['username'];
|
||||
$password = (isset($_data['password'])) ? trim($_data['password']) : $is_now['password'];
|
||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('relayhost_invalid', $id)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE `relayhosts` SET
|
||||
`hostname` = :hostname,
|
||||
`username` = :username,
|
||||
`password` = :password,
|
||||
`active` = :active
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(
|
||||
':id' => $id,
|
||||
':hostname' => $hostname,
|
||||
':username' => $username,
|
||||
':password' => $password,
|
||||
':active' => $active
|
||||
));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('object_modified', htmlspecialchars(implode(', ', $hostnames)))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$ids = (array)$_data['id'];
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
$stmt = $pdo->prepare("DELETE FROM `relayhosts` WHERE `id`= :id");
|
||||
$stmt->execute(array(':id' => $id));
|
||||
$stmt = $pdo->prepare("UPDATE `domain` SET `relayhost` = '0' WHERE `relayhost`= :id");
|
||||
$stmt->execute(array(':id' => $id));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('relayhost_removed', htmlspecialchars($id))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'get':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
return false;
|
||||
}
|
||||
$relayhosts = array();
|
||||
$stmt = $pdo->query("SELECT `id`, `hostname`, `username` FROM `relayhosts`");
|
||||
$relayhosts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $relayhosts;
|
||||
break;
|
||||
case 'details':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
|
||||
return false;
|
||||
}
|
||||
$relayhostdata = array();
|
||||
$stmt = $pdo->prepare("SELECT `id`,
|
||||
`hostname`,
|
||||
`username`,
|
||||
`password`,
|
||||
`active` AS `active_int`,
|
||||
CONCAT(LEFT(`password`, 3), '...') AS `password_short`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM `relayhosts`
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(':id' => $_data));
|
||||
$relayhostdata = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!empty($relayhostdata)) {
|
||||
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(`domain` SEPARATOR ', ') AS `used_by_domains` FROM `domain` WHERE `relayhost` = :id");
|
||||
$stmt->execute(array(':id' => $_data));
|
||||
$used_by_domains = $stmt->fetch(PDO::FETCH_ASSOC)['used_by_domains'];
|
||||
$used_by_domains = (empty($used_by_domains)) ? '' : $used_by_domains;
|
||||
$relayhostdata['used_by_domains'] = $used_by_domains;
|
||||
}
|
||||
return $relayhostdata;
|
||||
break;
|
||||
}
|
||||
}
|
||||
function transport($_action, $_data = null) {
|
||||
global $pdo;
|
||||
global $lang;
|
||||
$_data_log = $_data;
|
||||
switch ($_action) {
|
||||
case 'add':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$destination = trim($_data['destination']);
|
||||
$nexthop = trim($_data['nexthop']);
|
||||
preg_match('/\[(.+)\].*/', $nexthop, $next_hop_matches);
|
||||
$next_hop_clean = (isset($next_hop_matches[1])) ? $next_hop_matches[1] : $nexthop;
|
||||
$username = str_replace(':', '\:', trim($_data['username']));
|
||||
$password = str_replace(':', '\:', trim($_data['password']));
|
||||
// ".domain" is a valid destination, "..domain" is not
|
||||
if (empty($destination) || (is_valid_domain_name(preg_replace('/^' . preg_quote('.', '/') . '/', '', $destination)) === false && $destination != '*')) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'invalid_destination'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (empty($nexthop)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('invalid_nexthop')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$transports = transport('get');
|
||||
if (!empty($transports)) {
|
||||
foreach ($transports as $transport) {
|
||||
$transport_data = transport('details', $transport['id']);
|
||||
$existing_nh[] = $transport_data['nexthop'];
|
||||
preg_match('/\[(.+)\].*/', $transport_data['nexthop'], $existing_clean_nh[]);
|
||||
if (($transport_data['nexthop'] == $nexthop || $transport_data['nexthop'] == $next_hop_clean) && $transport_data['username'] != $username) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'invalid_nexthop_authenticated'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($next_hop_matches[1])) {
|
||||
if (in_array($next_hop_clean, $existing_nh)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('next_hop_interferes', $next_hop_clean, $nexthop)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($existing_clean_nh as $existing_clean_nh_each) {
|
||||
if ($existing_clean_nh_each[1] == $nexthop) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('next_hop_interferes_any', $nexthop)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("INSERT INTO `transports` (`nexthop`, `destination`, `username` ,`password`, `active`)
|
||||
VALUES (:nexthop, :destination, :username, :password, :active)");
|
||||
$stmt->execute(array(
|
||||
':nexthop' => $nexthop,
|
||||
':destination' => $destination,
|
||||
':username' => $username,
|
||||
':password' => str_replace(':', '\:', $password),
|
||||
':active' => '1'
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `transports` SET
|
||||
`username` = :username,
|
||||
`password` = :password
|
||||
WHERE `nexthop` = :nexthop");
|
||||
$stmt->execute(array(
|
||||
':nexthop' => $nexthop,
|
||||
':username' => $username,
|
||||
':password' => $password
|
||||
));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('relayhost_added', htmlspecialchars(implode(', ', $hosts)))
|
||||
);
|
||||
break;
|
||||
case 'edit':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$ids = (array)$_data['id'];
|
||||
foreach ($ids as $id) {
|
||||
$is_now = transport('details', $id);
|
||||
if (!empty($is_now)) {
|
||||
$destination = (!empty($_data['destination'])) ? trim($_data['destination']) : $is_now['destination'];
|
||||
$nexthop = (!empty($_data['nexthop'])) ? trim($_data['nexthop']) : $is_now['nexthop'];
|
||||
$username = (isset($_data['username'])) ? trim($_data['username']) : $is_now['username'];
|
||||
$password = (isset($_data['password'])) ? trim($_data['password']) : $is_now['password'];
|
||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('relayhost_invalid', $id)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
preg_match('/\[(.+)\].*/', $nexthop, $next_hop_matches);
|
||||
$next_hop_clean = (isset($next_hop_matches[1])) ? $next_hop_matches[1] : $nexthop;
|
||||
$transports = transport('get');
|
||||
if (!empty($transports)) {
|
||||
foreach ($transports as $transport) {
|
||||
$transport_data = transport('details', $transport['id']);
|
||||
if ($transport['id'] == $id) {
|
||||
continue;
|
||||
}
|
||||
$existing_nh[] = $transport_data['nexthop'];
|
||||
preg_match('/\[(.+)\].*/', $transport_data['nexthop'], $existing_clean_nh[]);
|
||||
}
|
||||
}
|
||||
if (isset($next_hop_matches[1])) {
|
||||
if (in_array($next_hop_clean, $existing_nh)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('next_hop_interferes', $next_hop_clean, $nexthop)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($existing_clean_nh as $existing_clean_nh_each) {
|
||||
if ($existing_clean_nh_each[1] == $nexthop) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('next_hop_interferes_any', $nexthop)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($username)) {
|
||||
$password = '';
|
||||
}
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE `transports` SET
|
||||
`destination` = :destination,
|
||||
`nexthop` = :nexthop,
|
||||
`username` = :username,
|
||||
`password` = :password,
|
||||
`active` = :active
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(
|
||||
':id' => $id,
|
||||
':destination' => $destination,
|
||||
':nexthop' => $nexthop,
|
||||
':username' => $username,
|
||||
':password' => $password,
|
||||
':active' => $active
|
||||
));
|
||||
$stmt = $pdo->prepare("UPDATE `transports` SET
|
||||
`username` = :username,
|
||||
`password` = :password
|
||||
WHERE `nexthop` = :nexthop");
|
||||
$stmt->execute(array(
|
||||
':nexthop' => $nexthop,
|
||||
':username' => $username,
|
||||
':password' => $password
|
||||
));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('object_modified', htmlspecialchars(implode(', ', $hostnames)))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$ids = (array)$_data['id'];
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
$stmt = $pdo->prepare("DELETE FROM `transports` WHERE `id`= :id");
|
||||
$stmt->execute(array(':id' => $id));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||
'msg' => array('relayhost_removed', htmlspecialchars($id))
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'get':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
return false;
|
||||
}
|
||||
$transports = array();
|
||||
$stmt = $pdo->query("SELECT `id`, `destination`, `nexthop`, `username` FROM `transports`");
|
||||
$transports = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $transports;
|
||||
break;
|
||||
case 'details':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
|
||||
return false;
|
||||
}
|
||||
$transportdata = array();
|
||||
$stmt = $pdo->prepare("SELECT `id`,
|
||||
`destination`,
|
||||
`nexthop`,
|
||||
`username`,
|
||||
`password`,
|
||||
`active` AS `active_int`,
|
||||
CONCAT(LEFT(`password`, 3), '...') AS `password_short`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM `transports`
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(':id' => $_data));
|
||||
$transportdata = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $transportdata;
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ function init_db_schema() {
|
|||
try {
|
||||
global $pdo;
|
||||
|
||||
$db_version = "14112018_0717";
|
||||
$db_version = "15122018_0717";
|
||||
|
||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
|
@ -109,6 +109,26 @@ function init_db_schema() {
|
|||
),
|
||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||
),
|
||||
"transports" => array(
|
||||
"cols" => array(
|
||||
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||
"destination" => "VARCHAR(255) NOT NULL",
|
||||
"nexthop" => "VARCHAR(255) NOT NULL",
|
||||
"username" => "VARCHAR(255) NOT NULL",
|
||||
"password" => "VARCHAR(255) NOT NULL",
|
||||
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
||||
),
|
||||
"keys" => array(
|
||||
"primary" => array(
|
||||
"" => array("id")
|
||||
),
|
||||
"key" => array(
|
||||
"destination" => array("destination"),
|
||||
"nexthop" => array("nexthop"),
|
||||
)
|
||||
),
|
||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||
),
|
||||
"alias" => array(
|
||||
"cols" => array(
|
||||
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||
|
|
|
@ -147,7 +147,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.mailq.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.ratelimit.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.relayhost.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.transports.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.rsettings.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.tls_policy_maps.inc.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fail2ban.inc.php';
|
||||
|
|
|
@ -5,10 +5,12 @@ jQuery(function($){
|
|||
var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};
|
||||
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
|
||||
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
|
||||
function hashCode(t){for(var n=0,r=0;r<t.length;r++)n=t.charCodeAt(r)+((n<<5)-n);return n}
|
||||
function intToRGB(t){var n=(16777215&t).toString(16).toUpperCase();return"00000".substring(0,6-n.length)+n}
|
||||
$("#rspamd_preset_1").on('click', function(e) {
|
||||
e.preventDefault();
|
||||
$("form[data-id=rsetting]").find("#adminRspamdSettingsDesc").val(lang.rsettings_preset_1);
|
||||
$("form[data-id=rsetting]").find("#adminRspamdSettingsContent").val('priority = 10;\nauthenticated = yes;\napply "default" {\n symbols_enabled = ["DKIM_SIGNED", "RATELIMIT_UPDATE", "RATELIMIT_CHECK", "DYN_RL_CHECK", "HISTORY_SAVE", "MILTER_HEADERS", "ARC_SIGNED"];\n}');
|
||||
$("form[data-id=rsetting]").find("#adminRspamdSettingsContent").val('priority = 10;\nauthenticated = yes;\napply "default" {\n symbols_enabled = ["DKIM_SIGNED", "RATELIMITED", "RATELIMIT_UPDATE", "RATELIMIT_CHECK", "DYN_RL_CHECK", "HISTORY_SAVE", "MILTER_HEADERS", "ARC_SIGNED"];\n}');
|
||||
});
|
||||
$("#rspamd_preset_2").on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
@ -145,7 +147,7 @@ jQuery(function($){
|
|||
{"name":"username","title":lang.username,"breakpoints":"xs sm"},
|
||||
{"name":"used_by_domains","title":lang.in_use_by,"style":{"width":"110px"}, "type": "text","breakpoints":"xs sm"},
|
||||
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
|
||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"280px","width":"280px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
|
||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"220px","width":"220px"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
|
||||
],
|
||||
"rows": $.ajax({
|
||||
dataType: 'json',
|
||||
|
@ -163,6 +165,33 @@ jQuery(function($){
|
|||
"sorting": {"enabled": true}
|
||||
});
|
||||
}
|
||||
function draw_transport_maps() {
|
||||
ft_relayhoststable = FooTable.init('#transportstable', {
|
||||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"name":"id","type":"text","title":"ID","style":{"width":"50px"}},
|
||||
{"name":"destination","type":"text","title":lang.destination,"style":{"width":"250px"}},
|
||||
{"name":"nexthop","type":"text","title":lang.nexthop,"style":{"width":"250px"}},
|
||||
{"name":"username","title":lang.username,"breakpoints":"xs sm"},
|
||||
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
|
||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"220px","width":"220px"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
|
||||
],
|
||||
"rows": $.ajax({
|
||||
dataType: 'json',
|
||||
url: '/api/v1/get/transport/all',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw transports table');
|
||||
},
|
||||
success: function (data) {
|
||||
return process_table_data(data, 'transportstable');
|
||||
}
|
||||
}),
|
||||
"empty": lang.empty,
|
||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||
"sorting": {"enabled": true}
|
||||
});
|
||||
}
|
||||
function draw_queue() {
|
||||
ft_queuetable = FooTable.init('#queuetable', {
|
||||
"columns": [
|
||||
|
@ -205,12 +234,24 @@ jQuery(function($){
|
|||
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="#" data-toggle="modal" data-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="sender-dependent" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-triangle-right"></span> Test</a>' +
|
||||
'<a href="/edit/relayhost/' + encodeURI(item.id) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
||||
'<a href="#" data-action="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>' +
|
||||
'<a href="#" data-action="delete_selected" data-id="single-rlyhost" 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 == 'transportstable') {
|
||||
$.each(data, function (i, item) {
|
||||
if (item.username) {
|
||||
item.username = '<span style="border-left:3px solid #' + intToRGB(hashCode(item.nexthop)) + ';padding-left:5px;">' + item.username + '</span>';
|
||||
}
|
||||
item.action = '<div class="btn-group">' +
|
||||
'<a href="#" data-toggle="modal" data-target="#testTransportModal" data-transport-id="' + encodeURI(item.id) + '" data-transport-type="transport-map" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-triangle-right"></span> Test</a>' +
|
||||
'<a href="/edit/transport/' + encodeURI(item.id) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
||||
'<a href="#" data-action="delete_selected" data-id="single-transport" data-api-url="delete/transport" 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="transports" name="multi_select" value="' + item.id + '" />';
|
||||
});
|
||||
} else if (table == 'queuetable') {
|
||||
$.each(data, function (i, item) {
|
||||
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
|
||||
|
@ -264,6 +305,7 @@ jQuery(function($){
|
|||
draw_admins();
|
||||
draw_fwd_hosts();
|
||||
draw_relayhosts();
|
||||
draw_transport_maps();
|
||||
draw_queue();
|
||||
// Relayhost
|
||||
$('#testRelayhostModal').on('show.bs.modal', function (e) {
|
||||
|
@ -290,6 +332,32 @@ jQuery(function($){
|
|||
}
|
||||
});
|
||||
})
|
||||
// Transport
|
||||
$('#testTransportModal').on('show.bs.modal', function (e) {
|
||||
$('#test_transport_result').text("-");
|
||||
button = $(e.relatedTarget)
|
||||
if (button != null) {
|
||||
$('#transport_id').val(button.data('transport-id'));
|
||||
$('#transport_type').val(button.data('transport-type'));
|
||||
}
|
||||
})
|
||||
$('#test_transport').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
prev = $('#test_transport').text();
|
||||
$(this).prop("disabled",true);
|
||||
$(this).html('<span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ');
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'inc/ajax/transport_check.php',
|
||||
dataType: 'text',
|
||||
data: $('#test_transport_form').serialize(),
|
||||
complete: function (data) {
|
||||
$('#test_transport_result').html(data.responseText);
|
||||
$('#test_transport').prop("disabled",false);
|
||||
$('#test_transport').text(prev);
|
||||
}
|
||||
});
|
||||
})
|
||||
// DKIM private key modal
|
||||
$('#showDKIMprivKey').on('show.bs.modal', function (e) {
|
||||
$('#priv_key_pre').text("-");
|
||||
|
|
|
@ -8,6 +8,8 @@ jQuery(function($){
|
|||
var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};
|
||||
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
|
||||
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
|
||||
function hashCode(t){for(var n=0,r=0;r<t.length;r++)n=t.charCodeAt(r)+((n<<5)-n);return n}
|
||||
function intToRGB(t){var n=(16777215&t).toString(16).toUpperCase();return"00000".substring(0,6-n.length)+n}
|
||||
$(".refresh_table").on('click', function(e) {
|
||||
e.preventDefault();
|
||||
var table_name = $(this).data('table');
|
||||
|
@ -162,6 +164,48 @@ jQuery(function($){
|
|||
}
|
||||
});
|
||||
}
|
||||
function draw_rl_logs() {
|
||||
ft_rl_logs = FooTable.init('#rl_log', {
|
||||
"columns": [
|
||||
{"name":"indicator","title":" ","style":{"width":"50px"}},
|
||||
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.last_applied,"style":{"width":"170px"}},
|
||||
{"name":"rl_name","title":lang.rate_name},
|
||||
{"name":"from","title":lang.sender},
|
||||
{"name":"rcpt","title":lang.recipients},
|
||||
{"name":"user","title":lang.authed_user},
|
||||
{"name":"message_id","title":"Msg ID","breakpoints": "all","style":{"word-break":"break-all"}},
|
||||
{"name":"header_from","title":"Header From","breakpoints": "all","style":{"word-break":"break-all"}},
|
||||
{"name":"header_subject","title":"Subject","breakpoints": "all","style":{"word-break":"break-all"}},
|
||||
{"name":"rl_hash","title":"Hash","breakpoints": "all","style":{"word-break":"break-all"}},
|
||||
{"name":"qid","title":"Rspamd QID","breakpoints": "all","style":{"word-break":"break-all"}},
|
||||
{"name":"ip","title":"IP","breakpoints": "all","style":{"word-break":"break-all"}},
|
||||
{"name":"action","title":lang.action,"breakpoints": "all","style":{"word-break":"break-all"}},
|
||||
],
|
||||
"rows": $.ajax({
|
||||
dataType: 'json',
|
||||
url: '/api/v1/get/logs/ratelimited',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw rl log table');
|
||||
},
|
||||
success: function (data) {
|
||||
return process_table_data(data, 'rllog');
|
||||
}
|
||||
}),
|
||||
"empty": lang.empty,
|
||||
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
|
||||
"filtering": {"enabled": true,"delay": 1,"position": "left","connectors": false,"placeholder": lang.filter_table},
|
||||
"sorting": {"enabled": true},
|
||||
"on": {
|
||||
"ready.ft.table": function(e, ft){
|
||||
table_log_ready(ft, 'rl_logs');
|
||||
},
|
||||
"after.ft.paging": function(e, ft){
|
||||
table_log_paging(ft, 'rl_logs');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function draw_ui_logs() {
|
||||
ft_api_logs = FooTable.init('#ui_logs', {
|
||||
"columns": [
|
||||
|
@ -540,6 +584,19 @@ jQuery(function($){
|
|||
item.method = '<span class="label label-warning">' + item.method + '</span>';
|
||||
}
|
||||
});
|
||||
} else if (table == 'rllog') {
|
||||
$.each(data, function (i, item) {
|
||||
if (item.user == null) {
|
||||
item.user = "none";
|
||||
}
|
||||
if (item.rl_hash == null) {
|
||||
item.rl_hash = "err";
|
||||
}
|
||||
item.indicator = '<span style="border-right:6px solid #' + intToRGB(hashCode(item.rl_hash)) + ';padding-left:5px;"> </span>';
|
||||
if (item.rl_hash != 'err') {
|
||||
item.action = '<a href="#" data-action="delete_selected" data-id="single-hash" data-api-url="delete/rlhash" data-item="' + encodeURI(item.rl_hash) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.reset_limit + '</a>';
|
||||
}
|
||||
});
|
||||
}
|
||||
return data
|
||||
};
|
||||
|
@ -575,6 +632,7 @@ jQuery(function($){
|
|||
draw_watchdog_logs();
|
||||
draw_acme_logs();
|
||||
draw_api_logs();
|
||||
draw_rl_logs();
|
||||
draw_ui_logs();
|
||||
draw_netfilter_logs();
|
||||
draw_rspamd_history();
|
||||
|
|
|
@ -646,6 +646,67 @@ jQuery(function($){
|
|||
}
|
||||
});
|
||||
}
|
||||
function draw_transport_maps_table() {
|
||||
ft_transport_maps_table = FooTable.init('#transport_maps_table', {
|
||||
"columns": [
|
||||
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
|
||||
{"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}},
|
||||
{"name":"dest","title":lang.tls_map_dest},
|
||||
{"name":"parameters","title":lang.tls_map_parameters},
|
||||
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
|
||||
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":(role == "admin" ? lang.action : ""),"breakpoints":"xs sm"}
|
||||
],
|
||||
"empty": lang.empty,
|
||||
"rows": $.ajax({
|
||||
dataType: 'json',
|
||||
url: '/api/v1/get/transport-map/all',
|
||||
jsonp: false,
|
||||
error: function () {
|
||||
console.log('Cannot draw transport map table');
|
||||
},
|
||||
success: function (data) {
|
||||
if (role == "admin") {
|
||||
$.each(data, function (i, item) {
|
||||
item.dest = escapeHtml(item.dest);
|
||||
if (item.parameters == '') {
|
||||
item.parameters = '<code>-</code>';
|
||||
} else {
|
||||
item.parameters = '<code>' + escapeHtml(item.parameters) + '</code>';
|
||||
}
|
||||
item.action = '<div class="btn-group">' +
|
||||
'<a href="/edit/transport_map/' + item.id + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
|
||||
'<a href="#" data-action="delete_selected" data-id="single-transport-map" data-api-url="delete/transport-map" data-item="' + item.id + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
|
||||
'</div>';
|
||||
item.chkbox = '<input type="checkbox" data-id="transport-map" name="multi_select" value="' + item.id + '" />';
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
"paging": {
|
||||
"enabled": true,
|
||||
"limit": 5,
|
||||
"size": pagination_size
|
||||
},
|
||||
"filtering": {
|
||||
"enabled": true,
|
||||
"delay": 100,
|
||||
"position": "left",
|
||||
"connectors": false,
|
||||
"placeholder": lang.filter_table
|
||||
},
|
||||
"sorting": {
|
||||
"enabled": true
|
||||
},
|
||||
"on": {
|
||||
"ready.ft.table": function(e, ft){
|
||||
table_mailbox_ready(ft, 'transport_maps_table');
|
||||
},
|
||||
"after.ft.paging": function(e, ft){
|
||||
paging_mailbox_after(ft, 'transport_maps_table');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function draw_alias_table() {
|
||||
ft_alias_table = FooTable.init('#alias_table', {
|
||||
"columns": [
|
||||
|
@ -925,5 +986,6 @@ jQuery(function($){
|
|||
draw_bcc_table();
|
||||
draw_recipient_map_table();
|
||||
draw_tls_policy_table();
|
||||
draw_transport_maps_table();
|
||||
|
||||
});
|
||||
|
|
|
@ -102,6 +102,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
case "relayhost":
|
||||
process_add_return(relayhost('add', $attr));
|
||||
break;
|
||||
case "transport":
|
||||
process_add_return(transport('add', $attr));
|
||||
break;
|
||||
case "rsetting":
|
||||
process_add_return(rsettings('add', $attr));
|
||||
break;
|
||||
|
@ -320,6 +323,33 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
}
|
||||
break;
|
||||
|
||||
case "transport":
|
||||
switch ($object) {
|
||||
case "all":
|
||||
$transports = transport('get');
|
||||
if (!empty($transports)) {
|
||||
foreach ($transports as $transport) {
|
||||
if ($details = transport('details', $transport['id'])) {
|
||||
$data[] = $details;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
process_get_return($data);
|
||||
}
|
||||
else {
|
||||
echo '{}';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$data = transport('details', $object);
|
||||
process_get_return($data);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case "rsetting":
|
||||
switch ($object) {
|
||||
case "all":
|
||||
|
@ -387,6 +417,17 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
}
|
||||
echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
|
||||
break;
|
||||
case "ratelimited":
|
||||
// 0 is first record, so empty is fine
|
||||
if (isset($extra)) {
|
||||
$extra = preg_replace('/[^\d\-]/i', '', $extra);
|
||||
$logs = get_logs('ratelimited', $extra);
|
||||
}
|
||||
else {
|
||||
$logs = get_logs('ratelimited');
|
||||
}
|
||||
echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}';
|
||||
break;
|
||||
case "netfilter":
|
||||
// 0 is first record, so empty is fine
|
||||
if (isset($extra)) {
|
||||
|
@ -979,6 +1020,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
case "relayhost":
|
||||
process_delete_return(relayhost('delete', array('id' => $items)));
|
||||
break;
|
||||
case "transport":
|
||||
process_delete_return(transport('delete', array('id' => $items)));
|
||||
break;
|
||||
case "rsetting":
|
||||
process_delete_return(rsettings('delete', array('id' => $items)));
|
||||
break;
|
||||
|
@ -1043,6 +1087,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
case "admin":
|
||||
process_delete_return(admin('delete', array('username' => $items)));
|
||||
break;
|
||||
case "rlhash":
|
||||
echo ratelimit('delete', null, implode($items));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "edit":
|
||||
|
@ -1093,6 +1140,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
|
|||
case "relayhost":
|
||||
process_edit_return(relayhost('edit', array_merge(array('id' => $items), $attr)));
|
||||
break;
|
||||
case "transport":
|
||||
process_edit_return(transport('edit', array_merge(array('id' => $items), $attr)));
|
||||
break;
|
||||
case "rsetting":
|
||||
process_edit_return(rsettings('edit', array_merge(array('id' => $items), $attr)));
|
||||
break;
|
||||
|
|
|
@ -33,6 +33,11 @@ $lang['success']['verified_u2f_login'] = "U2F přihlášení ověřeno";
|
|||
$lang['success']['verified_yotp_login'] = "Yubico OTP přihlášení ověřeno";
|
||||
$lang['danger']['yotp_verification_failed'] = "Yubico OTP ověření selhalo: %s";
|
||||
$lang['danger']['ip_list_empty'] = "Seznam povolených IP nesmí být prázdný";
|
||||
$lang['danger']['invalid_destination'] = "Formát cíle je špatný";
|
||||
$lang['danger']['invalid_nexthop'] = "Formát skoku (Next hop) je špatný";
|
||||
$lang['danger']['invalid_nexthop_authenticated'] = "Skok (Next hop) již existuje s rozílným přihlašovacím údajem, nejdříve prosím aktualizujte existující přihlašovací údaje tohoto skoku.";
|
||||
$lang['danger']['next_hop_interferes'] = "%s koliduje se skokem %s";
|
||||
$lang['danger']['next_hop_interferes_any'] = "Existující skok koliduje s %s";
|
||||
$lang['danger']['rspamd_ui_pw_length'] = "Heslo pro Rspamd UI musí být minimálně 6 znaků dlouhé";
|
||||
$lang['success']['rspamd_ui_pw_set'] = "Heslo k Rspamd UI nastaveno";
|
||||
$lang['success']['queue_command_success'] = "Příkaz pro frontu úspěšně dokončen";
|
||||
|
@ -251,7 +256,7 @@ $lang['user']['create_syncjob'] = 'Vytvořit novou synchronizační úlohu';
|
|||
|
||||
$lang['start']['mailcow_apps_detail'] = 'Použijte mailcow aplikace pro přístup k vašim e-mailům, kalendáři, kontaktům a dalším funkcím.';
|
||||
$lang['start']['mailcow_panel_detail'] = '<b>Administrátoři domén</b> mohou vytvářet, upravovat nebo mazat mailboxy a aliasy. Dále mohou upravovat parametry domény a zobrazovat další informace o jejich přidělených doménách.<br>
|
||||
<b>Uživatelé<b> mohou vytvářet časově omezené aliasy (spam aliases), měnit jejich heslo a nastavovat spam filtr.';
|
||||
<b>Uživatelé</b> mohou vytvářet časově omezené aliasy (spam aliases), měnit jejich heslo a nastavovat spam filtr.';
|
||||
$lang['start']['imap_smtp_server_auth_info'] = 'Použijte prosím vaší celou e-mailovou adresu a způsob ověřování PLAIN.<br>
|
||||
Vaše přihlašovací údaje budou zašifrovány na straně serveru.';
|
||||
$lang['start']['help'] = 'Zobrazit/Skrýt panel nápovědy';
|
||||
|
@ -331,7 +336,6 @@ Každý filtr bude zpracován v daném pořadí. Ani chyba při vykonávání sk
|
|||
Prefilter → Uživatelské skripty → 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'] = 'Není použitelná žádná akce';
|
||||
|
||||
|
||||
$lang['edit']['syncjob'] = 'Upravit synchronizační úlohu';
|
||||
$lang['edit']['client_id'] = 'ID klienta';
|
||||
$lang['edit']['client_secret'] = 'Client secret';
|
||||
|
@ -387,6 +391,7 @@ $lang['edit']['dont_check_sender_acl'] = "Disable sender check for domain %s (+
|
|||
$lang['edit']['multiple_bookings'] = 'Vícenásobné rezervace';
|
||||
$lang['edit']['kind'] = 'Druh';
|
||||
$lang['edit']['resource'] = 'Zdroje';
|
||||
$lang['edit']['relayhost'] = 'Přeposílání závislé na odesílateli';
|
||||
|
||||
$lang['acl']['spam_alias'] = 'Dočasné aliasy';
|
||||
$lang['acl']['tls_policy'] = 'Pravidla TLS';
|
||||
|
@ -400,14 +405,17 @@ $lang['acl']['quarantine'] = 'Karanténa';
|
|||
$lang['acl']['login_as'] = 'Přihlásit jako uživatel poštovní schránky';
|
||||
$lang['acl']['bcc_maps'] = 'BCC maps';
|
||||
$lang['acl']['filters'] = 'Filtry';
|
||||
$lang['acl']['ratelimit'] = 'Omezování provozu (Rate limit)';
|
||||
$lang['acl']['ratelimit'] = 'Omezování provozu';
|
||||
$lang['acl']['recipient_maps'] = 'Recipient maps';
|
||||
$lang['acl']['prohibited'] = 'Zakázáno z důvodu ACL';
|
||||
|
||||
$lang['add']['generate'] = 'generovat';
|
||||
$lang['add']['syncjob'] = 'Přidat synchronizační úlohu';
|
||||
$lang['add']['syncjob_hint'] = 'Upozornění: Heslo bude uloženo v prostém textu!';
|
||||
$lang['add']['hostname'] = 'Jméno hostitele (Hostname)';
|
||||
$lang['add']['hostname'] = 'Jméno hostitele (Host)';
|
||||
$lang['add']['destination'] = 'Cíl';
|
||||
$lang['add']['nexthop'] = 'Další skok (Next hop)';
|
||||
$lang['edit']['nexthop'] = 'Další skok (Next hop)';
|
||||
$lang['add']['port'] = 'Port';
|
||||
$lang['add']['username'] = 'Jméno uživatele';
|
||||
$lang['add']['enc_method'] = 'Metoda šifrování';
|
||||
|
@ -474,7 +482,6 @@ $lang['mailbox']['sync_jobs'] = 'Synchronizační úlohy';
|
|||
$lang['mailbox']['inactive'] = 'Neaktivní';
|
||||
$lang['edit']['validate_save'] = 'Ověřit a uložit';
|
||||
|
||||
|
||||
$lang['login']['username'] = 'Jméno uživatele';
|
||||
$lang['login']['password'] = 'Heslo';
|
||||
$lang['login']['login'] = 'Přihlásit';
|
||||
|
@ -582,24 +589,46 @@ $lang['admin']['no_record'] = 'Žádný záznam';
|
|||
$lang['admin']['filter_table'] = 'Tabulka filtrů';
|
||||
$lang['admin']['empty'] = 'Žádné výsledky';
|
||||
$lang['admin']['time'] = 'Čas';
|
||||
$lang['admin']['last_applied'] = 'Naposledy použité';
|
||||
$lang['admin']['reset_limit'] = 'Odebrat hash';
|
||||
$lang['admin']['hash_remove_info'] = 'Odebrání hashe omezování provozu (pokud stále existuje) vyresetuje kompletně jeho čítač.<br>
|
||||
Každý hash je označen jedinečnou barvou.';
|
||||
$lang['warning']['hash_not_found'] = 'Hash nenalezen';
|
||||
$lang['success']['hash_deleted'] = 'Hash byl smazán';
|
||||
$lang['admin']['authed_user'] = 'Přihlášený uživatel';
|
||||
$lang['admin']['priority'] = 'Priorita';
|
||||
$lang['admin']['message'] = 'Zpráva';
|
||||
$lang['admin']['rate_name'] = 'Název (Rate name)';
|
||||
$lang['admin']['refresh'] = 'Obnovit';
|
||||
$lang['admin']['to_top'] = 'Zpět na začátek';
|
||||
$lang['admin']['in_use_by'] = 'Používáno';
|
||||
$lang['admin']['forwarding_hosts'] = 'Přesměrování klientů (Forwarding Hosts)';
|
||||
$lang['admin']['forwarding_hosts_hint'] = 'Příchozí zprávy jsou bezpodmínečně akceptovány od všech zde uvedených klientů. Tito klienti nebudou kontrolováni proti DNSBL nebo podrobeni greylistingu. Spam obdržený od těchto klientů nebude nikdy odmítnut, ale příležitostně může být uložen do složky se spamem. Nejčastějším účelem je zadat poštovní servery, pro které jste nastavili pravidlo, které předává příchozí e-maily na váš poštovní server.';
|
||||
$lang['admin']['forwarding_hosts_add_hint'] = 'Lze zadat IPv4/IPv6 adresy, sítě ve formátu CIDR, názvy klientů (které budou převedeny na IP adresy) nebo názvy domén (které budou převedeny na IP dotazováním se DNS na SPF záznam nebo pokud neexistuje tak na MX záznam).';
|
||||
$lang['admin']['relayhosts_hint'] = 'Nastavte zde nadřazené SMTP servery (relayhosts), pro možnost je vybrat v okně pro nastavení domény.';
|
||||
$lang['admin']['add_relayhost_add_hint'] = 'Upozornění: autentizační data budou uložena jako prostý text.';
|
||||
$lang['admin']['relayhosts_hint'] = 'Nastavte zde přeposílání závislé na odesílateli (sender-dependent transports), pro možnost je vybrat v okně pro nastavení domény.<br>
|
||||
Služba přeposílání je vždy "smtp:". Individuální uživatelská nastavení odchozích TLS politik jsou také povolena.';
|
||||
$lang['admin']['transports_hint'] = 'Položky seznamu přeposílání (transport map) <b>přetěžují</b> položky seznamu přeposílání závislém na odesílateli (sender-dependent transport)</b>.<br>
|
||||
Individuální uživatelská nastavení odchozích TLS politik jsou ignorována a lze je vynutit přes přetěžování odchozích TLS pravidel (TLS policy map overrides). Služba přeposílání je vždy "smtp:".<br>
|
||||
Pro zjištění přihlašovacích údajů dalšího skoku "[host]:25" se Postfix <b>vždy</b> dotáže na "nexthop" před hledáním "[nexthop]:25". Toto chování znemožnuje použít "nexthop" a "[nexthop]:25" současně.';
|
||||
$lang['admin']['add_relayhost_hint'] = 'Upozornění: přihlašovací údaje (pokud existují) budou uloženy jako prostý text.';
|
||||
$lang['admin']['add_transports_hint'] = 'Upozornění: přihlašovací údaje budou uloženy jako prostý text.';
|
||||
$lang['admin']['host'] = 'Klient (Host)';
|
||||
$lang['admin']['source'] = 'Zdroj';
|
||||
$lang['admin']['add_forwarding_host'] = 'Přidat přesměrování klientů (Forwarding Hosts)';
|
||||
$lang['admin']['add_relayhost'] = 'Přidat nadřazený SMTP server (Relayhost)';
|
||||
$lang['admin']['add_relayhost'] = 'Přidat přeposílání závislé na odesílateli (sender-dependent transport)';
|
||||
$lang['admin']['add_transport'] = 'Přidat přeposílání (Transport)';
|
||||
$lang['admin']['relayhosts'] = 'Přeposílání závislé na odesílateli (Sender-dependent transports)';
|
||||
$lang['admin']['transport_maps'] = 'Přeposílání (Transport Maps)';
|
||||
$lang['admin']['routing'] = 'Směrování';
|
||||
$lang['admin']['credentials_transport_warning'] = '<b>Upozornění</b>: Přidání položky do seznamu přeposílání aktualizuje také přihlašovací údaje všech záznamů s odpovídajícím sloupcem (nexthop).';
|
||||
|
||||
$lang['admin']['destination'] = 'Cíl';
|
||||
$lang['admin']['nexthop'] = 'Další skok (Next hop)';
|
||||
|
||||
$lang['success']['forwarding_host_removed'] = "Přesměrovaný klient %s byl odebrán";
|
||||
$lang['success']['forwarding_host_added'] = "Přesměrovaný klient %s byl přidán";
|
||||
$lang['success']['relayhost_removed'] = "Nadřazený SMTP server (Relayhost) %s byl odebrán";
|
||||
$lang['success']['relayhost_added'] = "Nadřazený SMTP server (Relayhost) %s byl přidán";
|
||||
$lang['success']['relayhost_removed'] = "Položka seznamu přeposílání %s byla odebrána";
|
||||
$lang['success']['relayhost_added'] = "Položky seznamu přeposílání %s byla přidána";
|
||||
$lang['diagnostics']['dns_records'] = 'DNS záznamy';
|
||||
$lang['diagnostics']['dns_records_24hours'] = 'Upozornění: Změnám provedeným v systému DNS může trvat až 24 hodin, než se na této stránce správně zobrazí jejich aktuální stav. Tato stránka je určena pro snadné zjištění, jak nakonfigurovat DNS záznamy a zda jsou všechny vaše záznamy správně uloženy.';
|
||||
$lang['diagnostics']['dns_records_name'] = 'Název';
|
||||
|
@ -624,6 +653,9 @@ $lang['admin']['quarantine'] = "Karanténa";
|
|||
$lang['admin']['quarantine_retention_size'] = "Počet zadržených zpráv na poštovní schránku<br />0 znamená <b>neaktivní</b>!";
|
||||
$lang['admin']['quarantine_max_size'] = "Maximální velikost v MiB (větší prvky budou smazány)<br />0 <b>neznamená</b> neomezeno!";
|
||||
$lang['admin']['quarantine_exclude_domains'] = "Vyloučené domény a doménové aliasy:";
|
||||
$lang['admin']['quarantine_release_format'] = "Formát propuštěných položek:";
|
||||
$lang['admin']['quarantine_release_format_raw'] = "Nezměněný originál";
|
||||
$lang['admin']['quarantine_release_format_att'] = "Jako příloha";
|
||||
|
||||
$lang['admin']['ui_texts'] = "Úpravy UI textů";
|
||||
$lang['admin']['help_text'] = "Přetíží text nápovědy pod přihlašovacím formulářem (HTML povoleno)";
|
||||
|
|
|
@ -29,6 +29,11 @@ $lang['success']['verified_u2f_login'] = "U2F Anmeldung verifiziert";
|
|||
$lang['success']['verified_yotp_login'] = "Yubico OTP Anmeldung verifiziert";
|
||||
$lang['danger']['yotp_verification_failed'] = "Yubico OTP Verifizierung fehlgeschlagen: %s";
|
||||
$lang['danger']['ip_list_empty'] = "Liste erlaubter IPs darf nicht leer sein";
|
||||
$lang['danger']['invalid_destination'] = "Ziel-Format ist ungültig";
|
||||
$lang['danger']['invalid_nexthop'] = "Next Hop ist ungültig";
|
||||
$lang['danger']['invalid_nexthop_authenticated'] = 'Dieser Next Hop existiert bereits mit abweichenden Authentifizierungsdaten. Die bestehenden Authentifizierungsdaten dieses "Next Hops" müssen vorab angepasst werden.';
|
||||
$lang['danger']['next_hop_interferes'] = "%s verhindert das Hinzufügen von Next Hop %s";
|
||||
$lang['danger']['next_hop_interferes_any'] = "Ein vorhandener Eintrag verhindert das Hinzufügen von Next Hop %s";
|
||||
$lang['danger']['rspamd_ui_pw_length'] = "Rspamd UI Passwort muss mindestens 6 Zeichen lang sein";
|
||||
$lang['success']['rspamd_ui_pw_set'] = "Rspamd UI Passwort wurde gesetzt";
|
||||
$lang['success']['queue_command_success'] = "Queue-Aufgabe erfolgreich ausgeführt";
|
||||
|
@ -65,7 +70,7 @@ $lang['success']['settings_map_added'] = "Regel wurde gespeichert";
|
|||
$lang['danger']['settings_map_invalid'] = "Regel ID %s ist ungültig";
|
||||
$lang['success']['settings_map_removed'] = "Regeln wurden entfernt: %s";
|
||||
$lang['danger']['invalid_host'] = "Ungültiger Host: %s";
|
||||
$lang['danger']['relayhost_invalid'] = "Relayhost %s ist ungültig";
|
||||
$lang['danger']['relayhost_invalid'] = "Mapeintrag %s ist ungültig";
|
||||
$lang['success']['saved_settings'] = "Regel wurde gespeichert";
|
||||
|
||||
$lang['danger']['dkim_domain_or_sel_invalid'] = 'DKIM-Domain oder Selektor nicht korrekt: %s';
|
||||
|
@ -392,7 +397,10 @@ $lang['acl']['prohibited'] = 'Untersagt durch Richtlinie';
|
|||
$lang['add']['generate'] = 'generieren';
|
||||
$lang['add']['syncjob'] = 'Syncjob hinzufügen';
|
||||
$lang['add']['syncjob_hint'] = 'Passwörter werden unverschlüsselt abgelegt!';
|
||||
$lang['add']['hostname'] = 'Servername';
|
||||
$lang['add']['hostname'] = 'Host';
|
||||
$lang['add']['destination'] = 'Ziel';
|
||||
$lang['add']['nexthop'] = 'Next Hop';
|
||||
$lang['edit']['nexthop'] = 'Next Hop';
|
||||
$lang['add']['port'] = 'Port';
|
||||
$lang['add']['username'] = 'Benutzername';
|
||||
$lang['add']['enc_method'] = 'Verschlüsselung';
|
||||
|
@ -556,20 +564,46 @@ $lang['admin']['no_record'] = 'Kein Eintrag';
|
|||
$lang['admin']['filter_table'] = 'Tabelle Filtern';
|
||||
$lang['admin']['empty'] = 'Keine Einträge vorhanden';
|
||||
$lang['admin']['time'] = 'Zeit';
|
||||
$lang['admin']['last_applied'] = 'Zuletzt angewendet';
|
||||
$lang['admin']['reset_limit'] = 'Hash entfernen';
|
||||
$lang['admin']['hash_remove_info'] = 'Das Entfernen eines Ratelimit Hashes - sofern noch existent - bewirkt den Reset gezählter Nachrichten dieses Elements.<br>
|
||||
Jeder Hash wird durch eine eindeutige Farbe gekennzeichnet.';
|
||||
$lang['warning']['hash_not_found'] = 'Hash nicht gefunden';
|
||||
$lang['success']['hash_deleted'] = 'Hash wurde gelöscht';
|
||||
$lang['admin']['authed_user'] = 'Auth. Benutzer';
|
||||
$lang['admin']['priority'] = 'Gewichtung';
|
||||
$lang['admin']['refresh'] = 'Neu laden';
|
||||
$lang['admin']['to_top'] = 'Nach oben';
|
||||
$lang['admin']['in_use_by'] = 'Verwendet von';
|
||||
$lang['admin']['rate_name'] = 'Rate name';
|
||||
$lang['admin']['message'] = 'Nachricht';
|
||||
$lang['admin']['forwarding_hosts'] = 'Weiterleitungs-Hosts';
|
||||
$lang['admin']['forwarding_hosts_hint'] = 'Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt, optional kann er aber in den Spam-Ordner einsortiert werden. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem mailcow-Server eingerichtet wurde.';
|
||||
$lang['admin']['forwarding_hosts_add_hint'] = 'Sie können entweder IPv4/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.';
|
||||
$lang['admin']['relayhosts_hint'] = 'Erstellen Sie Relayhosts, um diese im Einstellungsdialog einer Domain auszuwählen.';
|
||||
$lang['admin']['add_relayhost_add_hint'] = 'Bitte beachten Sie, dass Relayhost Anmeldedaten im Klartext gespeichert werden.';
|
||||
$lang['admin']['relayhosts_hint'] = 'Erstellen Sie senderabhängige Transporte, um diese im Einstellungsdialog einer Domain auszuwählen.<br>
|
||||
Der Transporttyp lautet immer "smtp:". Benutzereinstellungen bezüglich Verschlüsselungsrichtlinie werden beim Transport berücksichtigt.';
|
||||
$lang['admin']['transports_hint'] = 'Transport Maps <b>überwiegen</b> senderabhängige Transport Maps und ignorieren die individuellen Einstellungen eines Benutzers bezüglich Verschlüsselungsrichtlinie, da der Absender bei Ermittlung der Transportregel nicht berücksichtigt wird.<br>
|
||||
Der Transport erfolgt immer via "smtp:".<br>
|
||||
Ein Eintrag in der TLS Policy Map kann eine Verschlüsselung erzwingen.<br>
|
||||
Die Authentifizierung wird anhand des Host Parameters ermittelt, hierbei würde bei einem beispielhaften Next Hop "[host]:25" immer zuerst "host" abfragt und <b>erst im Anschluss</b> "[host]:25".<br>
|
||||
Dieses Verhalten schließt die <b>gleichzeitige Verwendung</b> von Einträgen der Art "host" sowie "[host]:25" aus.';
|
||||
$lang['admin']['add_relayhost_hint'] = 'Bitte beachten Sie, dass Anmeldedaten klartext gespeichert werden.<br>
|
||||
Angelegte Transporte dieser Art sind <b>senderabhängig</b> und müssen erst einer Domain zugewiesen werden, bevor sie als Transport verwendet werden.<br>
|
||||
Diese Einstellungen entsprechen demach <i>nicht</i> dem "relayhost" Parameter in Postfix.';
|
||||
$lang['admin']['add_transports_hint'] = 'Bitte beachten Sie, dass Anmeldedaten klartext gespeichert werden.';
|
||||
$lang['admin']['host'] = 'Host';
|
||||
$lang['admin']['source'] = 'Quelle';
|
||||
$lang['admin']['add_forwarding_host'] = 'Weiterleitungs-Host hinzufügen';
|
||||
$lang['admin']['add_relayhost'] = 'Relayhost hinzufügen';
|
||||
$lang['admin']['add_relayhost'] = 'Senderabhängigen Transport hinzufügen';
|
||||
$lang['admin']['add_transport'] = 'Transport hinzufügen';
|
||||
$lang['admin']['relayhosts'] = 'Senderabhängige Transport Maps';
|
||||
$lang['admin']['transport_maps'] = 'Transport Maps';
|
||||
$lang['admin']['routing'] = 'Routing';
|
||||
$lang['admin']['credentials_transport_warning'] = '<b>Warnung</b>: Das Hinzufügen einer neuen Regel bewirkt die Aktualisierung der Authentifizierungsdaten aller vorhandenen Einträge mit identischem Host.';
|
||||
|
||||
$lang['admin']['destination'] = 'Ziel';
|
||||
$lang['admin']['nexthop'] = 'Next Hop';
|
||||
|
||||
$lang['admin']['api_allow_from'] = "IP-Adressen für Zugriff";
|
||||
$lang['admin']['api_key'] = "API-Key";
|
||||
$lang['admin']['activate_api'] = "API aktivieren";
|
||||
|
@ -586,8 +620,8 @@ $lang['admin']['quarantine_exclude_domains'] = "Domains und Alias-Domains aussch
|
|||
|
||||
$lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entfernt";
|
||||
$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['success']['relayhost_removed'] = "Mapeintrag %s wurde entfernt";
|
||||
$lang['success']['relayhost_added'] = "Mapeintrag %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 anzuzeigen und zu überprüfen, ob die Daten im DNS hinterlegt sind.';
|
||||
$lang['diagnostics']['dns_records_name'] = 'Name';
|
||||
|
|
|
@ -30,6 +30,11 @@ $lang['success']['verified_u2f_login'] = "Verified U2F login";
|
|||
$lang['success']['verified_yotp_login'] = "Verified Yubico OTP login";
|
||||
$lang['danger']['yotp_verification_failed'] = "Yubico OTP verification failed: %s";
|
||||
$lang['danger']['ip_list_empty'] = "List of allowed IPs cannot be empty";
|
||||
$lang['danger']['invalid_destination'] = "Destination format is invalid";
|
||||
$lang['danger']['invalid_nexthop'] = "Next hop format is invalid";
|
||||
$lang['danger']['invalid_nexthop_authenticated'] = "Next hops exists with different credentials, please update the existing credentials for this next hop first.";
|
||||
$lang['danger']['next_hop_interferes'] = "%s interferes with nexthop %s";
|
||||
$lang['danger']['next_hop_interferes_any'] = "An existing next hop interferes with %s";
|
||||
$lang['danger']['rspamd_ui_pw_length'] = "Rspamd UI password should be at least 6 chars long";
|
||||
$lang['success']['rspamd_ui_pw_set'] = "Rspamd UI password successfully set";
|
||||
$lang['success']['queue_command_success'] = "Queue command completed successfully";
|
||||
|
@ -66,7 +71,7 @@ $lang['success']['settings_map_added'] = "Added settings map entry";
|
|||
$lang['danger']['settings_map_invalid'] = "Settings map ID %s invalid";
|
||||
$lang['success']['settings_map_removed'] = "Removed settings map ID %s";
|
||||
$lang['danger']['invalid_host'] = "Invalid host specified: %s";
|
||||
$lang['danger']['relayhost_invalid'] = "Relayhost %s is invalid";
|
||||
$lang['danger']['relayhost_invalid'] = "Map entry %s is invalid";
|
||||
$lang['success']['saved_settings'] = "Saved settings";
|
||||
$lang['success']['db_init_complete'] = "Database initialization completed";
|
||||
|
||||
|
@ -384,6 +389,7 @@ $lang['edit']['dont_check_sender_acl'] = "Disable sender check for domain %s (+
|
|||
$lang['edit']['multiple_bookings'] = 'Multiple bookings';
|
||||
$lang['edit']['kind'] = 'Kind';
|
||||
$lang['edit']['resource'] = 'Resource';
|
||||
$lang['edit']['relayhost'] = 'Sender-dependent transports';
|
||||
|
||||
$lang['acl']['spam_alias'] = 'Temporary aliases';
|
||||
$lang['acl']['tls_policy'] = 'TLS policy';
|
||||
|
@ -405,7 +411,10 @@ $lang['acl']['prohibited'] = 'Prohibited by ACL';
|
|||
$lang['add']['generate'] = 'generate';
|
||||
$lang['add']['syncjob'] = 'Add sync job';
|
||||
$lang['add']['syncjob_hint'] = 'Be aware that passwords need to be saved plain-text!';
|
||||
$lang['add']['hostname'] = 'Hostname';
|
||||
$lang['add']['hostname'] = 'Host';
|
||||
$lang['add']['destination'] = 'Destination';
|
||||
$lang['add']['nexthop'] = 'Next hop';
|
||||
$lang['edit']['nexthop'] = 'Next hop';
|
||||
$lang['add']['port'] = 'Port';
|
||||
$lang['add']['username'] = 'Username';
|
||||
$lang['add']['enc_method'] = 'Encryption method';
|
||||
|
@ -580,24 +589,46 @@ $lang['admin']['no_record'] = 'No record';
|
|||
$lang['admin']['filter_table'] = 'Filter table';
|
||||
$lang['admin']['empty'] = 'No results';
|
||||
$lang['admin']['time'] = 'Time';
|
||||
$lang['admin']['last_applied'] = 'Last applied';
|
||||
$lang['admin']['reset_limit'] = 'Remove hash';
|
||||
$lang['admin']['hash_remove_info'] = 'Removing a ratelimit hash (if still existing) will reset its counter completely.<br>
|
||||
Each hash is indicated by an individual color.';
|
||||
$lang['warning']['hash_not_found'] = 'Hash not found';
|
||||
$lang['success']['hash_deleted'] = 'Hash deleted';
|
||||
$lang['admin']['authed_user'] = 'Auth. user';
|
||||
$lang['admin']['priority'] = 'Priority';
|
||||
$lang['admin']['message'] = 'Message';
|
||||
$lang['admin']['rate_name'] = 'Rate name';
|
||||
$lang['admin']['refresh'] = 'Refresh';
|
||||
$lang['admin']['to_top'] = 'Back to top';
|
||||
$lang['admin']['in_use_by'] = 'In use by';
|
||||
$lang['admin']['forwarding_hosts'] = 'Forwarding Hosts';
|
||||
$lang['admin']['forwarding_hosts_hint'] = 'Incoming messages are unconditionally accepted from any hosts listed here. These hosts are then not checked against DNSBLs or subjected to greylisting. Spam received from them is never rejected, but optionally it can be filed into the Junk folder. The most common use for this is to specify mail servers on which you have set up a rule that forwards incoming emails to your mailcow server.';
|
||||
$lang['admin']['forwarding_hosts_add_hint'] = 'You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).';
|
||||
$lang['admin']['relayhosts_hint'] = 'Define relayhosts here to be able to select them in a domains configuration dialog.';
|
||||
$lang['admin']['add_relayhost_add_hint'] = 'Please be aware that relayhost authentication data will be stored as plain text.';
|
||||
$lang['admin']['relayhosts_hint'] = 'Define sender-dependent transports to be able to select them in a domains configuration dialog.<br>
|
||||
The transport service is always "smtp:". A users individual outbound TLS policy setting is taken into account.';
|
||||
$lang['admin']['transports_hint'] = 'A transport map entry <b>overrules</b> a sender-dependent transport map</b>.<br>
|
||||
Outbound TLS policy settings per-user are ignored and can only be enfored by TLS policy map entries. The transport service is always "smtp:".<br>
|
||||
To determine credentials for an exemplary next hop "[host]:25", Postfix <b>always</b> queries for "nexthop" before searching for "[nexthop]:25". This behavior makes it impossible to use "nexthop" and "[nexthop]:25" at the same time.';
|
||||
$lang['admin']['add_relayhost_hint'] = 'Please be aware that authentication data, if any, will be stored as plain text.';
|
||||
$lang['admin']['add_transports_hint'] = 'Please be aware that authentication data, if any, will be stored as plain text.';
|
||||
$lang['admin']['host'] = 'Host';
|
||||
$lang['admin']['source'] = 'Source';
|
||||
$lang['admin']['add_forwarding_host'] = 'Add Forwarding Host';
|
||||
$lang['admin']['add_relayhost'] = 'Add Relayhost';
|
||||
$lang['admin']['add_forwarding_host'] = 'Add forwarding host';
|
||||
$lang['admin']['add_relayhost'] = 'Add sender-dependent transport';
|
||||
$lang['admin']['add_transport'] = 'Add transport';
|
||||
$lang['admin']['relayhosts'] = 'Sender-dependent transports';
|
||||
$lang['admin']['transport_maps'] = 'Transport Maps';
|
||||
$lang['admin']['routing'] = 'Routing';
|
||||
$lang['admin']['credentials_transport_warning'] = '<b>Warning</b>: Adding a new transport map entry will update the credentials for all entries with a matching nexthop column.';
|
||||
|
||||
$lang['admin']['destination'] = 'Destination';
|
||||
$lang['admin']['nexthop'] = 'Next hop';
|
||||
|
||||
$lang['success']['forwarding_host_removed'] = "Forwarding host %s has been removed";
|
||||
$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['success']['relayhost_removed'] = "Map entry %s has been removed";
|
||||
$lang['success']['relayhost_added'] = "Map entry %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';
|
||||
|
|
|
@ -411,8 +411,10 @@ $lang['edit']['delete1'] = 'Verwijder van oorsprong wanneer voltooid';
|
|||
$lang['edit']['delete2'] = 'Verwijder berichten die zich niet in de oorsprong bevinden';
|
||||
$lang['add']['custom_params'] = 'Aangepaste parameters';
|
||||
$lang['add']['subscribeall'] = 'Abonneer op alle mappen';
|
||||
$lang['add']['timeout1'] = 'Time-out voor verbinding met externe host';
|
||||
$lang['add']['timeout2'] = 'Time-out voor verbinding met lokale host';
|
||||
$lang['add']['timeout1'] = 'Time-out voor verbinding met externe hosts';
|
||||
$lang['add']['timeout2'] = 'Time-out voor verbinding met lokale hosts';
|
||||
$lang['edit']['timeout1'] = 'Time-out voor verbinding met externe hosts';
|
||||
$lang['edit']['timeout2'] = 'Time-out voor verbinding met lokale hosts';
|
||||
|
||||
$lang['add']['domain_matches_hostname'] = 'Domein %s komt overeen met hostname';
|
||||
$lang['add']['domain'] = 'Domein';
|
||||
|
@ -521,7 +523,7 @@ $lang['admin']['dkim_add_key'] = 'Voeg DKIM-sleutel toe';
|
|||
$lang['admin']['dkim_keys'] = 'DKIM-sleutels';
|
||||
$lang['admin']['dkim_private_key'] = 'Privésleutel';
|
||||
$lang['admin']['dkim_domains_wo_keys'] = "Selecteer domeinen met ontbrekende sleutels";
|
||||
$lang['admin']['dkim_domains_selector'] = "Onderdeel";
|
||||
$lang['admin']['dkim_domains_selector'] = "Noemer";
|
||||
$lang['admin']['add'] = 'Toevoegen';
|
||||
$lang['add']['add_domain_restart'] = 'Voeg domein toe en herstart SOGo';
|
||||
$lang['add']['add_domain_only'] = 'Voeg enkel domein toe';
|
||||
|
@ -553,7 +555,7 @@ $lang['admin']['flush_queue'] = 'Leeg wachtrij';
|
|||
$lang['admin']['delete_queue'] = 'Verwijder alles';
|
||||
$lang['admin']['queue_deliver_mail'] = 'Lever af';
|
||||
$lang['admin']['queue_hold_mail'] = 'Houd vast';
|
||||
$lang['admin']['queue_unhold_mail'] = 'Vrijgeven';
|
||||
$lang['admin']['queue_unhold_mail'] = 'Geef vrij';
|
||||
$lang['admin']['username'] = 'Gebruikersnaam';
|
||||
$lang['admin']['edit'] = 'Wijzig';
|
||||
$lang['admin']['remove'] = 'Verwijder';
|
||||
|
@ -608,6 +610,9 @@ $lang['admin']['quarantine'] = "Quarantaine";
|
|||
$lang['admin']['quarantine_retention_size'] = "Maximale retenties per postvak<br />Gebruik 0 om deze functionaliteit <b>uit te zetten</b>.";
|
||||
$lang['admin']['quarantine_max_size'] = "Maximale grootte in MiB (mail die de limiet overschrijdt zal worden verwijderd)<br />0 betekent <b>niet</b> onbeperkt!";
|
||||
$lang['admin']['quarantine_exclude_domains'] = "Sluit domeinen en aliasdomeinen uit:";
|
||||
$lang['admin']['quarantine_release_format'] = "Vrijgegeven items worden verstuurd als:";
|
||||
$lang['admin']['quarantine_release_format_raw'] = "Origineel";
|
||||
$lang['admin']['quarantine_release_format_att'] = "Bijlage";
|
||||
|
||||
$lang['admin']['ui_texts'] = "UI-labels en teksten";
|
||||
$lang['admin']['help_text'] = "Pas hulpteksten onder inlogvenster aan (HTML toegestaan)";
|
||||
|
@ -648,7 +653,7 @@ $lang['danger']['imagick_exception'] = "Error: Er is een probleem opgetreden met
|
|||
$lang['quarantine']['quarantine'] = "Quarantaine";
|
||||
$lang['quarantine']['learn_spam_delete'] = "Onthoud als spam en verwijder";
|
||||
$lang['quarantine']['qinfo'] = 'Het quarantainesysteem slaat geweigerde e-mail op, terwijl het voor de afzender lijkt alsof deze <em>niet</em> ontvangen is.<br>"' . $lang['quarantine']['learn_spam_delete'] . '" traint het systeem om soortgelijke e-mails in de toekomst direct als spam te markeren.<br>Wees er van bewust dat wanneer er meerdere berichten worden onderzocht, dit mogelijk enige tijd kan duren.';
|
||||
$lang['quarantine']['release'] = "Vrijgeven";
|
||||
$lang['quarantine']['release'] = "Geef vrij";
|
||||
$lang['quarantine']['empty'] = 'Geen resultaten';
|
||||
$lang['quarantine']['toggle_all'] = 'Selecteer alles';
|
||||
$lang['quarantine']['quick_actions'] = 'Handelingen';
|
||||
|
|
|
@ -151,17 +151,18 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
</div>
|
||||
</div>
|
||||
</div><!-- add admin modal -->
|
||||
<!-- test relayhost modal -->
|
||||
<div class="modal fade" id="testRelayhostModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<!-- test transport modal -->
|
||||
<div class="modal fade" id="testTransportModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||||
<h3 class="modal-title"><span class="glyphicon glyphicon-stats"></span> Relayhost</h3>
|
||||
<h3 class="modal-title"><span class="glyphicon glyphicon-stats"></span> Transport</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal" data-cached-form="true" id="test_relayhost_form" role="form" method="post">
|
||||
<input type="hidden" class="form-control" name="relayhost_id" id="relayhost_id">
|
||||
<form class="form-horizontal" data-cached-form="true" id="test_transport_form" role="form" method="post">
|
||||
<input type="hidden" class="form-control" name="transport_id" id="transport_id">
|
||||
<input type="hidden" class="form-control" name="transport_type" id="transport_type">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="mail_from"><?=$lang['admin']['relay_from'];?></label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -170,16 +171,16 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button class="btn btn-default" id="test_relayhost" href="#"><?=$lang['admin']['relay_run'];?></button>
|
||||
<button class="btn btn-default" id="test_transport" href="#"><?=$lang['admin']['relay_run'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div id="test_relayhost_result" style="font-size:10pt">-</div>
|
||||
<div id="test_transport_result" style="font-size:10pt">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- test relayhost modal -->
|
||||
</div><!-- test transport modal -->
|
||||
<!-- priv key modal -->
|
||||
<div class="modal fade" id="showDKIMprivKey" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
|
|
|
@ -131,7 +131,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="rl_frame">Ratelimit</label>
|
||||
<label class="control-label col-sm-2" for="rl_frame"><?=$lang['acl']['ratelimit'];?></label>
|
||||
<div class="col-sm-7">
|
||||
<input name="rl_value" type="number" value="<?=(!empty($rl['value'])) ? $rl['value'] : null;?>" class="form-control" placeholder="disabled">
|
||||
</div>
|
||||
|
@ -328,7 +328,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
|
|||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="rl_frame">Ratelimit</label>
|
||||
<label class="control-label col-sm-2" for="rl_frame"><?=$lang['acl']['ratelimit'];?></label>
|
||||
<div class="col-sm-7">
|
||||
<input name="rl_value" type="number" value="<?=(!empty($rl['value'])) ? $rl['value'] : null;?>" class="form-control" placeholder="disabled">
|
||||
</div>
|
||||
|
|
|
@ -40,7 +40,7 @@ services:
|
|||
- mysql
|
||||
|
||||
redis-mailcow:
|
||||
image: redis:4-alpine
|
||||
image: redis:5-alpine
|
||||
volumes:
|
||||
- redis-vol-1:/data/
|
||||
restart: always
|
||||
|
@ -72,7 +72,7 @@ services:
|
|||
- clamd
|
||||
|
||||
rspamd-mailcow:
|
||||
image: mailcow/rspamd:1.32
|
||||
image: mailcow/rspamd:1.33
|
||||
build: ./data/Dockerfiles/rspamd
|
||||
stop_grace_period: 30s
|
||||
depends_on:
|
||||
|
@ -95,7 +95,7 @@ services:
|
|||
- rspamd
|
||||
|
||||
php-fpm-mailcow:
|
||||
image: mailcow/phpfpm:1.26
|
||||
image: mailcow/phpfpm:1.29
|
||||
build: ./data/Dockerfiles/phpfpm
|
||||
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
||||
depends_on:
|
||||
|
@ -138,7 +138,7 @@ services:
|
|||
- phpfpm
|
||||
|
||||
sogo-mailcow:
|
||||
image: mailcow/sogo:1.44
|
||||
image: mailcow/sogo:1.49
|
||||
build: ./data/Dockerfiles/sogo
|
||||
environment:
|
||||
- DBNAME=${DBNAME}
|
||||
|
@ -163,7 +163,7 @@ services:
|
|||
- sogo
|
||||
|
||||
dovecot-mailcow:
|
||||
image: mailcow/dovecot:1.48
|
||||
image: mailcow/dovecot:1.52
|
||||
build: ./data/Dockerfiles/dovecot
|
||||
cap_add:
|
||||
- NET_BIND_SERVICE
|
||||
|
@ -208,7 +208,7 @@ services:
|
|||
- dovecot
|
||||
|
||||
postfix-mailcow:
|
||||
image: mailcow/postfix:1.27
|
||||
image: mailcow/postfix:1.29
|
||||
build: ./data/Dockerfiles/postfix
|
||||
volumes:
|
||||
- ./data/conf/postfix:/opt/postfix/conf
|
||||
|
@ -344,7 +344,7 @@ services:
|
|||
- /lib/modules:/lib/modules:ro
|
||||
|
||||
watchdog-mailcow:
|
||||
image: mailcow/watchdog:1.29
|
||||
image: mailcow/watchdog:1.32
|
||||
# Debug
|
||||
#command: /watchdog.sh
|
||||
build: ./data/Dockerfiles/watchdog
|
||||
|
@ -393,6 +393,22 @@ services:
|
|||
- dockerapi
|
||||
|
||||
ipv6nat:
|
||||
depends_on:
|
||||
- unbound-mailcow
|
||||
- mysql-mailcow
|
||||
- redis-mailcow
|
||||
- clamd-mailcow
|
||||
- rspamd-mailcow
|
||||
- php-fpm-mailcow
|
||||
- sogo-mailcow
|
||||
- dovecot-mailcow
|
||||
- postfix-mailcow
|
||||
- memcached-mailcow
|
||||
- nginx-mailcow
|
||||
- acme-mailcow
|
||||
- netfilter-mailcow
|
||||
- watchdog-mailcow
|
||||
- dockerapi-mailcow
|
||||
image: robbertkl/ipv6nat
|
||||
restart: always
|
||||
privileged: true
|
||||
|
|
|
@ -159,7 +159,7 @@ USE_WATCHDOG=n
|
|||
|
||||
LOG_LINES=9999
|
||||
|
||||
# Internal IPv4 /24 subnet, format n.n.n. (expands to n.n.n.0/24)
|
||||
# Internal IPv4 /24 subnet, format n.n.n (expands to n.n.n.0/24)
|
||||
|
||||
IPV4_NETWORK=172.22.1
|
||||
|
||||
|
|
|
@ -32,7 +32,11 @@ 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'
|
||||
docker exec -it $(docker ps -f name=redis-mailcow -q) /bin/sh -c ' cat <<EOF | redis-cli
|
||||
SELECT 10
|
||||
FLUSHDB
|
||||
EOF
|
||||
'
|
||||
if [ -d ./data/web/nextcloud/config ]; then
|
||||
mv ./data/web/nextcloud/config/ ./data/conf/nextcloud-config-folder-$(date +%s).bak
|
||||
fi
|
||||
|
@ -87,6 +91,7 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
|
|||
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 redis timeout --value=0.0 --type=integer; \
|
||||
/web/nextcloud/occ config:system:set redis dbindex --value=10 --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_domains 1 --value=${MAILCOW_HOSTNAME}; \
|
||||
|
|
|
@ -263,6 +263,8 @@ docker-compose down
|
|||
# Silently fixing remote url from andryyy to mailcow
|
||||
git remote set-url origin https://github.com/mailcow/mailcow-dockerized
|
||||
echo -e "\e[32mCommitting current status...\e[0m"
|
||||
[[ -z "$(git config user.name)" ]] && git config user.name moo
|
||||
[[ -z "$(git config user.email)" ]] && git config user.email moo@cow.moo
|
||||
git update-index --assume-unchanged data/conf/rspamd/override.d/worker-controller-password.inc
|
||||
git add -u
|
||||
git commit -am "Before update on ${DATE}" > /dev/null
|
||||
|
|
Loading…
Reference in New Issue