Compare commits
100 Commits
feat/maria
...
2023-05
Author | SHA1 | Date | |
---|---|---|---|
|
74bcec45f1 | ||
|
9700b3251f | ||
|
88b8d50cd5 | ||
|
55b0191050 | ||
|
33c97fb318 | ||
|
23d33ad5a8 | ||
|
bd6c98047a | ||
|
73d6a29ae1 | ||
|
173e39c859 | ||
|
c0745c5cde | ||
|
1a6f93327e | ||
|
3c68a53170 | ||
|
e38c27ed67 | ||
|
8eaf8bbbde | ||
|
e015c7dbca | ||
|
58452abcdf | ||
|
2cbf0da137 | ||
|
aabcd10539 | ||
|
ee607dc3cc | ||
|
1265302a8e | ||
|
b5acf56e20 | ||
|
fe4a418af4 | ||
|
e5f03e8526 | ||
|
fb60c4a150 | ||
|
fd203abd47 | ||
|
6b65f0fc74 | ||
|
856b3b62f2 | ||
|
0372a2150d | ||
|
f3322c0577 | ||
|
c2bcc4e086 | ||
|
6e79c48640 | ||
|
d7dfa95e1b | ||
|
cf1cc24e33 | ||
|
6824a5650f | ||
|
73570cc8b5 | ||
|
959dcb9980 | ||
|
8f28666916 | ||
|
3eaa5a626c | ||
|
8c79056a94 | ||
|
ed076dc23e | ||
|
be2286c11c | ||
|
0e24c3d300 | ||
|
e1d8df6580 | ||
|
04a08a7d69 | ||
|
3c0c8aa01f | ||
|
026b278357 | ||
|
4121509ceb | ||
|
00ac61f0a4 | ||
|
4bb0dbb2f7 | ||
|
13b6df74af | ||
|
5c025bf865 | ||
|
20fc9eaf84 | ||
|
22a0479fab | ||
|
3510d5617d | ||
|
236d627fbd | ||
|
99739eada0 | ||
|
7bfef57894 | ||
|
d9dfe15253 | ||
|
3fe8aaa719 | ||
|
78a8fac6af | ||
|
6986e7758f | ||
|
b4a9df76b8 | ||
|
d9d958356a | ||
|
96f954a4e2 | ||
|
44585e1c15 | ||
|
c737ff4180 | ||
|
025279009d | ||
|
a9dc13d567 | ||
|
c3ed01c9b5 | ||
|
bd0b4a521e | ||
|
800a0ace71 | ||
|
db97869472 | ||
|
f681fcf154 | ||
|
db1b5956fc | ||
|
bdb07061ed | ||
|
428b917579 | ||
|
469f959e96 | ||
|
b68e189d97 | ||
|
028ef22878 | ||
|
80dacc015a | ||
|
0194c39bd5 | ||
|
f53ca24bb0 | ||
|
ae46a877d3 | ||
|
400939faf6 | ||
|
fd0205aafd | ||
|
e367a8ce24 | ||
|
096e2a41e9 | ||
|
e010f08143 | ||
|
3d2483ca37 | ||
|
26c34b484a | ||
|
4021613059 | ||
|
c28a6b89f0 | ||
|
1233613bea | ||
|
0206e0886c | ||
|
e5e6418be8 | ||
|
6507b53bbb | ||
|
f77c65411d | ||
|
1606658cb1 | ||
|
54ba66733e | ||
|
f6847e6f8c |
39
.github/workflows/update_postscreen_access_list.yml
vendored
Normal file
39
.github/workflows/update_postscreen_access_list.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Update postscreen_access.cidr
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Monthly
|
||||||
|
- cron: "0 0 1 * *"
|
||||||
|
workflow_dispatch: # Allow to run workflow manually
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Update-postscreen_access_cidr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Generate postscreen_access.cidr
|
||||||
|
run: |
|
||||||
|
bash helper-scripts/update_postscreen_whitelist.sh
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v5
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.mailcow_action_Update_postscreen_access_cidr_pat }}
|
||||||
|
commit-message: update postscreen_access.cidr
|
||||||
|
committer: milkmaker <milkmaker@mailcow.de>
|
||||||
|
author: milkmaker <milkmaker@mailcow.de>
|
||||||
|
signoff: false
|
||||||
|
branch: update/postscreen_access.cidr
|
||||||
|
base: staging
|
||||||
|
delete-branch: true
|
||||||
|
add-paths: |
|
||||||
|
data/conf/postfix/postscreen_access.cidr
|
||||||
|
title: '[Postfix] update postscreen_access.cidr'
|
||||||
|
body: |
|
||||||
|
This PR updates the postscreen_access.cidr using GitHub Actions and [helper-scripts/update_postscreen_whitelist.sh](https://github.com/mailcow/mailcow-dockerized/blob/master/helper-scripts/update_postscreen_whitelist.sh)
|
@@ -9,6 +9,7 @@ import os
|
|||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import redis
|
import redis
|
||||||
|
import platform
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
from logging.config import dictConfig
|
from logging.config import dictConfig
|
||||||
@@ -370,7 +371,7 @@ class DockerUtils:
|
|||||||
return exec_run_handler('utf8_text_only', sieve_return)
|
return exec_run_handler('utf8_text_only', sieve_return)
|
||||||
# api call: container_post - post_action: exec - cmd: sieve - task: print
|
# api call: container_post - post_action: exec - cmd: sieve - task: print
|
||||||
def container_post__exec__sieve__print(self, container_id, request_json):
|
def container_post__exec__sieve__print(self, container_id, request_json):
|
||||||
if 'username' in request.json and 'script_name' in request_json:
|
if 'username' in request_json and 'script_name' in request_json:
|
||||||
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]
|
cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]
|
||||||
sieve_return = container.exec_run(cmd)
|
sieve_return = container.exec_run(cmd)
|
||||||
@@ -380,7 +381,15 @@ class DockerUtils:
|
|||||||
if 'maildir' in request_json:
|
if 'maildir' in request_json:
|
||||||
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
sane_name = re.sub(r'\W+', '', request_json['maildir'])
|
sane_name = re.sub(r'\W+', '', request_json['maildir'])
|
||||||
cmd = ["/bin/bash", "-c", "if [[ -d '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' ]]; then /bin/mv '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"]
|
vmail_name = request_json['maildir'].replace("'", "'\\''")
|
||||||
|
cmd_vmail = "if [[ -d '/var/vmail/" + vmail_name + "' ]]; then /bin/mv '/var/vmail/" + vmail_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"
|
||||||
|
index_name = request_json['maildir'].split("/")
|
||||||
|
if len(index_name) > 1:
|
||||||
|
index_name = index_name[1].replace("'", "'\\''") + "@" + index_name[0].replace("'", "'\\''")
|
||||||
|
cmd_vmail_index = "if [[ -d '/var/vmail_index/" + index_name + "' ]]; then /bin/mv '/var/vmail_index/" + index_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "_index'; fi"
|
||||||
|
cmd = ["/bin/bash", "-c", cmd_vmail + " && " + cmd_vmail_index]
|
||||||
|
else:
|
||||||
|
cmd = ["/bin/bash", "-c", cmd_vmail]
|
||||||
maildir_cleanup = container.exec_run(cmd, user='vmail')
|
maildir_cleanup = container.exec_run(cmd, user='vmail')
|
||||||
return exec_run_handler('generic', maildir_cleanup)
|
return exec_run_handler('generic', maildir_cleanup)
|
||||||
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
|
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
|
||||||
@@ -477,7 +486,8 @@ async def get_host_stats(wait=5):
|
|||||||
"swap": psutil.swap_memory()
|
"swap": psutil.swap_memory()
|
||||||
},
|
},
|
||||||
"uptime": time.time() - psutil.boot_time(),
|
"uptime": time.time() - psutil.boot_time(),
|
||||||
"system_time": system_time.strftime("%d.%m.%Y %H:%M:%S")
|
"system_time": system_time.strftime("%d.%m.%Y %H:%M:%S"),
|
||||||
|
"architecture": platform.machine()
|
||||||
}
|
}
|
||||||
|
|
||||||
redis_client.set('host_stats', json.dumps(host_stats), ex=10)
|
redis_client.set('host_stats', json.dumps(host_stats), ex=10)
|
||||||
|
@@ -64,28 +64,40 @@ def refreshF2boptions():
|
|||||||
global f2boptions
|
global f2boptions
|
||||||
global quit_now
|
global quit_now
|
||||||
global exit_code
|
global exit_code
|
||||||
|
|
||||||
|
f2boptions = {}
|
||||||
|
|
||||||
if not r.get('F2B_OPTIONS'):
|
if not r.get('F2B_OPTIONS'):
|
||||||
f2boptions = {}
|
f2boptions['ban_time'] = r.get('F2B_BAN_TIME')
|
||||||
f2boptions['ban_time'] = int
|
f2boptions['max_ban_time'] = r.get('F2B_MAX_BAN_TIME')
|
||||||
f2boptions['max_attempts'] = int
|
f2boptions['ban_time_increment'] = r.get('F2B_BAN_TIME_INCREMENT')
|
||||||
f2boptions['retry_window'] = int
|
f2boptions['max_attempts'] = r.get('F2B_MAX_ATTEMPTS')
|
||||||
f2boptions['netban_ipv4'] = int
|
f2boptions['retry_window'] = r.get('F2B_RETRY_WINDOW')
|
||||||
f2boptions['netban_ipv6'] = int
|
f2boptions['netban_ipv4'] = r.get('F2B_NETBAN_IPV4')
|
||||||
f2boptions['ban_time'] = r.get('F2B_BAN_TIME') or 1800
|
f2boptions['netban_ipv6'] = r.get('F2B_NETBAN_IPV6')
|
||||||
f2boptions['max_attempts'] = r.get('F2B_MAX_ATTEMPTS') or 10
|
|
||||||
f2boptions['retry_window'] = r.get('F2B_RETRY_WINDOW') or 600
|
|
||||||
f2boptions['netban_ipv4'] = r.get('F2B_NETBAN_IPV4') or 32
|
|
||||||
f2boptions['netban_ipv6'] = r.get('F2B_NETBAN_IPV6') or 128
|
|
||||||
r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
f2boptions = {}
|
|
||||||
f2boptions = json.loads(r.get('F2B_OPTIONS'))
|
f2boptions = json.loads(r.get('F2B_OPTIONS'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print('Error loading F2B options: F2B_OPTIONS is not json')
|
print('Error loading F2B options: F2B_OPTIONS is not json')
|
||||||
quit_now = True
|
quit_now = True
|
||||||
exit_code = 2
|
exit_code = 2
|
||||||
|
|
||||||
|
verifyF2boptions(f2boptions)
|
||||||
|
r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
|
||||||
|
|
||||||
|
def verifyF2boptions(f2boptions):
|
||||||
|
verifyF2boption(f2boptions,'ban_time', 1800)
|
||||||
|
verifyF2boption(f2boptions,'max_ban_time', 10000)
|
||||||
|
verifyF2boption(f2boptions,'ban_time_increment', True)
|
||||||
|
verifyF2boption(f2boptions,'max_attempts', 10)
|
||||||
|
verifyF2boption(f2boptions,'retry_window', 600)
|
||||||
|
verifyF2boption(f2boptions,'netban_ipv4', 32)
|
||||||
|
verifyF2boption(f2boptions,'netban_ipv6', 128)
|
||||||
|
|
||||||
|
def verifyF2boption(f2boptions, f2boption, f2bdefault):
|
||||||
|
f2boptions[f2boption] = f2boptions[f2boption] if f2boption in f2boptions and f2boptions[f2boption] is not None else f2bdefault
|
||||||
|
|
||||||
def refreshF2bregex():
|
def refreshF2bregex():
|
||||||
global f2bregex
|
global f2bregex
|
||||||
global quit_now
|
global quit_now
|
||||||
@@ -147,6 +159,7 @@ def ban(address):
|
|||||||
global lock
|
global lock
|
||||||
refreshF2boptions()
|
refreshF2boptions()
|
||||||
BAN_TIME = int(f2boptions['ban_time'])
|
BAN_TIME = int(f2boptions['ban_time'])
|
||||||
|
BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment'])
|
||||||
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
||||||
RETRY_WINDOW = int(f2boptions['retry_window'])
|
RETRY_WINDOW = int(f2boptions['retry_window'])
|
||||||
NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
|
NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
|
||||||
@@ -174,20 +187,16 @@ def ban(address):
|
|||||||
net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False)
|
net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False)
|
||||||
net = str(net)
|
net = str(net)
|
||||||
|
|
||||||
if not net in bans or time.time() - bans[net]['last_attempt'] > RETRY_WINDOW:
|
if not net in bans:
|
||||||
bans[net] = { 'attempts': 0 }
|
bans[net] = {'attempts': 0, 'last_attempt': 0, 'ban_counter': 0}
|
||||||
active_window = RETRY_WINDOW
|
|
||||||
else:
|
|
||||||
active_window = time.time() - bans[net]['last_attempt']
|
|
||||||
|
|
||||||
bans[net]['attempts'] += 1
|
bans[net]['attempts'] += 1
|
||||||
bans[net]['last_attempt'] = time.time()
|
bans[net]['last_attempt'] = time.time()
|
||||||
|
|
||||||
active_window = time.time() - bans[net]['last_attempt']
|
|
||||||
|
|
||||||
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
||||||
cur_time = int(round(time.time()))
|
cur_time = int(round(time.time()))
|
||||||
logCrit('Banning %s for %d minutes' % (net, BAN_TIME / 60))
|
NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter']
|
||||||
|
logCrit('Banning %s for %d minutes' % (net, NET_BAN_TIME / 60 ))
|
||||||
if type(ip) is ipaddress.IPv4Address:
|
if type(ip) is ipaddress.IPv4Address:
|
||||||
with lock:
|
with lock:
|
||||||
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
|
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
|
||||||
@@ -206,7 +215,7 @@ def ban(address):
|
|||||||
rule.target = target
|
rule.target = target
|
||||||
if rule not in chain.rules:
|
if rule not in chain.rules:
|
||||||
chain.insert_rule(rule)
|
chain.insert_rule(rule)
|
||||||
r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + BAN_TIME)
|
r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + NET_BAN_TIME)
|
||||||
else:
|
else:
|
||||||
logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
|
logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
|
||||||
|
|
||||||
@@ -238,7 +247,8 @@ def unban(net):
|
|||||||
r.hdel('F2B_ACTIVE_BANS', '%s' % net)
|
r.hdel('F2B_ACTIVE_BANS', '%s' % net)
|
||||||
r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
|
r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
|
||||||
if net in bans:
|
if net in bans:
|
||||||
del bans[net]
|
bans[net]['attempts'] = 0
|
||||||
|
bans[net]['ban_counter'] += 1
|
||||||
|
|
||||||
def permBan(net, unban=False):
|
def permBan(net, unban=False):
|
||||||
global lock
|
global lock
|
||||||
@@ -366,6 +376,8 @@ def snat4(snat_target):
|
|||||||
chain.insert_rule(new_rule)
|
chain.insert_rule(new_rule)
|
||||||
else:
|
else:
|
||||||
for position, rule in enumerate(chain.rules):
|
for position, rule in enumerate(chain.rules):
|
||||||
|
if not hasattr(rule.target, 'parameter'):
|
||||||
|
continue
|
||||||
match = all((
|
match = all((
|
||||||
new_rule.get_src() == rule.get_src(),
|
new_rule.get_src() == rule.get_src(),
|
||||||
new_rule.get_dst() == rule.get_dst(),
|
new_rule.get_dst() == rule.get_dst(),
|
||||||
@@ -425,6 +437,8 @@ def autopurge():
|
|||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
refreshF2boptions()
|
refreshF2boptions()
|
||||||
BAN_TIME = int(f2boptions['ban_time'])
|
BAN_TIME = int(f2boptions['ban_time'])
|
||||||
|
MAX_BAN_TIME = int(f2boptions['max_ban_time'])
|
||||||
|
BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment'])
|
||||||
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
||||||
QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
|
QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
|
||||||
if QUEUE_UNBAN:
|
if QUEUE_UNBAN:
|
||||||
@@ -432,7 +446,9 @@ def autopurge():
|
|||||||
unban(str(net))
|
unban(str(net))
|
||||||
for net in bans.copy():
|
for net in bans.copy():
|
||||||
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
||||||
if time.time() - bans[net]['last_attempt'] > BAN_TIME:
|
NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter']
|
||||||
|
TIME_SINCE_LAST_ATTEMPT = time.time() - bans[net]['last_attempt']
|
||||||
|
if TIME_SINCE_LAST_ATTEMPT > NET_BAN_TIME or TIME_SINCE_LAST_ATTEMPT > MAX_BAN_TIME:
|
||||||
unban(net)
|
unban(net)
|
||||||
|
|
||||||
def isIpNetwork(address):
|
def isIpNetwork(address):
|
||||||
|
@@ -172,6 +172,24 @@ BEGIN
|
|||||||
END;
|
END;
|
||||||
//
|
//
|
||||||
DELIMITER ;
|
DELIMITER ;
|
||||||
|
DROP EVENT IF EXISTS clean_sasl_log;
|
||||||
|
DELIMITER //
|
||||||
|
CREATE EVENT clean_sasl_log
|
||||||
|
ON SCHEDULE EVERY 1 DAY DO
|
||||||
|
BEGIN
|
||||||
|
DELETE sasl_log.* FROM sasl_log
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT username, service, MAX(datetime) AS lastdate
|
||||||
|
FROM sasl_log
|
||||||
|
GROUP BY username, service
|
||||||
|
) AS last ON sasl_log.username = last.username AND sasl_log.service = last.service
|
||||||
|
WHERE datetime < DATE_SUB(NOW(), INTERVAL 31 DAY) AND datetime < lastdate;
|
||||||
|
DELETE FROM sasl_log
|
||||||
|
WHERE username NOT IN (SELECT username FROM mailbox) AND
|
||||||
|
datetime < DATE_SUB(NOW(), INTERVAL 31 DAY);
|
||||||
|
END;
|
||||||
|
//
|
||||||
|
DELIMITER ;
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@@ -24,6 +24,11 @@ mail_plugins = </etc/dovecot/mail_plugins
|
|||||||
mail_attachment_fs = crypt:set_prefix=mail_crypt_global:posix:
|
mail_attachment_fs = crypt:set_prefix=mail_crypt_global:posix:
|
||||||
mail_attachment_dir = /var/attachments
|
mail_attachment_dir = /var/attachments
|
||||||
mail_attachment_min_size = 128k
|
mail_attachment_min_size = 128k
|
||||||
|
# Significantly speeds up very large mailboxes, but is only safe to enable if
|
||||||
|
# you do not manually modify the files in the `cur` directories in
|
||||||
|
# mailcowdockerized_vmail-vol-1.
|
||||||
|
# https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-performance/
|
||||||
|
maildir_very_dirty_syncs = yes
|
||||||
|
|
||||||
# Dovecot 2.2
|
# Dovecot 2.2
|
||||||
#ssl_protocols = !SSLv3
|
#ssl_protocols = !SSLv3
|
||||||
|
@@ -28,3 +28,4 @@
|
|||||||
#197695 2 #Domain names registrar REG.RU Ltd, Russia
|
#197695 2 #Domain names registrar REG.RU Ltd, Russia
|
||||||
#198068 2 #P.A.G.M. OU, Estonia
|
#198068 2 #P.A.G.M. OU, Estonia
|
||||||
#201942 5 #Soltia Consulting SL, Spain
|
#201942 5 #Soltia Consulting SL, Spain
|
||||||
|
#213373 4 #IP Connect Inc
|
@@ -3176,8 +3176,10 @@ paths:
|
|||||||
example:
|
example:
|
||||||
attr:
|
attr:
|
||||||
ban_time: "86400"
|
ban_time: "86400"
|
||||||
|
ban_time_increment: "1"
|
||||||
blacklist: "10.100.6.5/32,10.100.8.4/32"
|
blacklist: "10.100.6.5/32,10.100.8.4/32"
|
||||||
max_attempts: "5"
|
max_attempts: "5"
|
||||||
|
max_ban_time: "86400"
|
||||||
netban_ipv4: "24"
|
netban_ipv4: "24"
|
||||||
netban_ipv6: "64"
|
netban_ipv6: "64"
|
||||||
retry_window: "600"
|
retry_window: "600"
|
||||||
@@ -3191,11 +3193,17 @@ paths:
|
|||||||
description: the backlisted ips or hostnames separated by comma
|
description: the backlisted ips or hostnames separated by comma
|
||||||
type: string
|
type: string
|
||||||
ban_time:
|
ban_time:
|
||||||
description: the time a ip should be banned
|
description: the time an ip should be banned
|
||||||
type: number
|
type: number
|
||||||
|
ban_time_increment:
|
||||||
|
description: if the time of the ban should increase each time
|
||||||
|
type: boolean
|
||||||
max_attempts:
|
max_attempts:
|
||||||
description: the maximum numbe of wrong logins before a ip is banned
|
description: the maximum numbe of wrong logins before a ip is banned
|
||||||
type: number
|
type: number
|
||||||
|
max_ban_time:
|
||||||
|
description: the maximum time an ip should be banned
|
||||||
|
type: number
|
||||||
netban_ipv4:
|
netban_ipv4:
|
||||||
description: the networks mask to ban for ipv4
|
description: the networks mask to ban for ipv4
|
||||||
type: number
|
type: number
|
||||||
@@ -4113,10 +4121,12 @@ paths:
|
|||||||
response:
|
response:
|
||||||
value:
|
value:
|
||||||
ban_time: 604800
|
ban_time: 604800
|
||||||
|
ban_time_increment: 1
|
||||||
blacklist: |-
|
blacklist: |-
|
||||||
45.82.153.37/32
|
45.82.153.37/32
|
||||||
92.118.38.52/32
|
92.118.38.52/32
|
||||||
max_attempts: 1
|
max_attempts: 1
|
||||||
|
max_ban_time: 604800
|
||||||
netban_ipv4: 32
|
netban_ipv4: 32
|
||||||
netban_ipv6: 128
|
netban_ipv6: 128
|
||||||
perm_bans:
|
perm_bans:
|
||||||
|
@@ -342,6 +342,10 @@ div.dataTables_wrapper div.dt-row {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.dataTables_wrapper span.sorting-value {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
div.dataTables_scrollHead table.dataTable {
|
div.dataTables_scrollHead table.dataTable {
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
@@ -66,4 +66,6 @@ table tbody tr td input[type="checkbox"] {
|
|||||||
padding: .2em .4em .3em !important;
|
padding: .2em .4em .3em !important;
|
||||||
background-color: #ececec!important;
|
background-color: #ececec!important;
|
||||||
}
|
}
|
||||||
|
.badge.bg-info .bi {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
@@ -20,6 +20,11 @@ legend {
|
|||||||
background-color: #7a7a7a !important;
|
background-color: #7a7a7a !important;
|
||||||
border-color: #5c5c5c !important;
|
border-color: #5c5c5c !important;
|
||||||
}
|
}
|
||||||
|
.btn-dark {
|
||||||
|
color: #000 !important;;
|
||||||
|
background-color: #f6f6f6 !important;;
|
||||||
|
border-color: #ddd !important;;
|
||||||
|
}
|
||||||
.btn-check:checked+.btn-secondary, .btn-check:active+.btn-secondary, .btn-secondary:active, .btn-secondary.active, .show>.btn-secondary.dropdown-toggle {
|
.btn-check:checked+.btn-secondary, .btn-check:active+.btn-secondary, .btn-secondary:active, .btn-secondary.active, .show>.btn-secondary.dropdown-toggle {
|
||||||
border-color: #7a7a7a !important;
|
border-color: #7a7a7a !important;
|
||||||
}
|
}
|
||||||
|
@@ -49,7 +49,9 @@ function bcc($_action, $_data = null, $_attr = null) {
|
|||||||
}
|
}
|
||||||
elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) {
|
elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) {
|
||||||
$mailbox = mailbox('get', 'mailbox_details', $local_dest);
|
$mailbox = mailbox('get', 'mailbox_details', $local_dest);
|
||||||
if ($mailbox === false && array_key_exists($local_dest, array_merge($direct_aliases, $shared_aliases)) === false) {
|
$shared_aliases = mailbox('get', 'shared_aliases');
|
||||||
|
$direct_aliases = mailbox('get', 'direct_aliases');
|
||||||
|
if ($mailbox === false && in_array($local_dest, array_merge($direct_aliases, $shared_aliases)) === false) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
|
||||||
|
@@ -239,7 +239,9 @@ function fail2ban($_action, $_data = null) {
|
|||||||
$is_now = fail2ban('get');
|
$is_now = fail2ban('get');
|
||||||
if (!empty($is_now)) {
|
if (!empty($is_now)) {
|
||||||
$ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
|
$ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
|
||||||
|
$ban_time_increment = (isset($_data['ban_time_increment']) && $_data['ban_time_increment'] == "1") ? 1 : 0;
|
||||||
$max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['max_attempts']);
|
$max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['max_attempts']);
|
||||||
|
$max_ban_time = intval((isset($_data['max_ban_time'])) ? $_data['max_ban_time'] : $is_now['max_ban_time']);
|
||||||
$retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']);
|
$retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']);
|
||||||
$netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']);
|
$netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']);
|
||||||
$netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']);
|
$netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']);
|
||||||
@@ -256,6 +258,8 @@ function fail2ban($_action, $_data = null) {
|
|||||||
}
|
}
|
||||||
$f2b_options = array();
|
$f2b_options = array();
|
||||||
$f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
|
$f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
|
||||||
|
$f2b_options['ban_time_increment'] = ($ban_time_increment == 1) ? true : false;
|
||||||
|
$f2b_options['max_ban_time'] = ($max_ban_time < 60) ? 60 : $max_ban_time;
|
||||||
$f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
|
$f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
|
||||||
$f2b_options['netban_ipv6'] = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
|
$f2b_options['netban_ipv6'] = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
|
||||||
$f2b_options['netban_ipv4'] = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;
|
$f2b_options['netban_ipv4'] = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;
|
||||||
|
@@ -1015,20 +1015,58 @@ function formatBytes($size, $precision = 2) {
|
|||||||
}
|
}
|
||||||
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
|
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
|
||||||
}
|
}
|
||||||
function update_sogo_static_view() {
|
function update_sogo_static_view($mailbox = null) {
|
||||||
if (getenv('SKIP_SOGO') == "y") {
|
if (getenv('SKIP_SOGO') == "y") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
global $pdo;
|
global $pdo;
|
||||||
global $lang;
|
global $lang;
|
||||||
$stmt = $pdo->query("SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES
|
|
||||||
WHERE TABLE_NAME = 'sogo_view'");
|
$mailbox_exists = false;
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
if ($mailbox !== null) {
|
||||||
if ($num_results != 0) {
|
// Check if the mailbox exists
|
||||||
$stmt = $pdo->query("REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings`)
|
$stmt = $pdo->prepare("SELECT username FROM mailbox WHERE username = :mailbox AND active = '1'");
|
||||||
SELECT `c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings` from sogo_view");
|
$stmt->execute(array(':mailbox' => $mailbox));
|
||||||
$stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($row){
|
||||||
|
$mailbox_exists = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$query = "REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings`)
|
||||||
|
SELECT
|
||||||
|
mailbox.username,
|
||||||
|
mailbox.domain,
|
||||||
|
mailbox.username,
|
||||||
|
IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.force_pw_update')) = '0',
|
||||||
|
IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.sogo_access')) = 1, password, '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'),
|
||||||
|
'{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'),
|
||||||
|
mailbox.name,
|
||||||
|
mailbox.username,
|
||||||
|
IFNULL(GROUP_CONCAT(ga.aliases ORDER BY ga.aliases SEPARATOR ' '), ''),
|
||||||
|
IFNULL(gda.ad_alias, ''),
|
||||||
|
IFNULL(external_acl.send_as_acl, ''),
|
||||||
|
mailbox.kind,
|
||||||
|
mailbox.multiple_bookings
|
||||||
|
FROM
|
||||||
|
mailbox
|
||||||
|
LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username REGEXP CONCAT('(^|,)', mailbox.username, '($|,)')
|
||||||
|
LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username
|
||||||
|
LEFT OUTER JOIN grouped_sender_acl_external external_acl ON external_acl.username = mailbox.username
|
||||||
|
WHERE
|
||||||
|
mailbox.active = '1'";
|
||||||
|
|
||||||
|
if ($mailbox_exists) {
|
||||||
|
$query .= " AND mailbox.username = :mailbox";
|
||||||
|
$stmt = $pdo->prepare($query);
|
||||||
|
$stmt->execute(array(':mailbox' => $mailbox));
|
||||||
|
} else {
|
||||||
|
$query .= " GROUP BY mailbox.username";
|
||||||
|
$stmt = $pdo->query($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
|
||||||
|
|
||||||
flush_memcached();
|
flush_memcached();
|
||||||
}
|
}
|
||||||
function edit_user_account($_data) {
|
function edit_user_account($_data) {
|
||||||
|
@@ -1264,11 +1264,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_sogo_static_view($username);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
'msg' => array('mailbox_added', htmlspecialchars($username))
|
'msg' => array('mailbox_added', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
break;
|
break;
|
||||||
case 'resource':
|
case 'resource':
|
||||||
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
|
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
|
||||||
@@ -3130,7 +3132,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
'msg' => array('mailbox_modified', $username)
|
'msg' => array('mailbox_modified', $username)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
update_sogo_static_view($username);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
break;
|
break;
|
||||||
case 'mailbox_templates':
|
case 'mailbox_templates':
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
@@ -3960,6 +3965,39 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
}
|
}
|
||||||
return $aliasdomaindata;
|
return $aliasdomaindata;
|
||||||
break;
|
break;
|
||||||
|
case 'shared_aliases':
|
||||||
|
$shared_aliases = array();
|
||||||
|
$stmt = $pdo->query("SELECT `address` FROM `alias`
|
||||||
|
WHERE `goto` REGEXP ','
|
||||||
|
AND `address` NOT LIKE '@%'
|
||||||
|
AND `goto` != `address`");
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$domain = explode("@", $row['address'])[1];
|
||||||
|
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||||
|
$shared_aliases[] = $row['address'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $shared_aliases;
|
||||||
|
break;
|
||||||
|
case 'direct_aliases':
|
||||||
|
$direct_aliases = array();
|
||||||
|
$stmt = $pdo->query("SELECT `address` FROM `alias`
|
||||||
|
WHERE `goto` NOT LIKE '%,%'
|
||||||
|
AND `address` NOT LIKE '@%'
|
||||||
|
AND `goto` != `address`");
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$domain = explode("@", $row['address'])[1];
|
||||||
|
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||||
|
$direct_aliases[] = $row['address'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $direct_aliases;
|
||||||
|
break;
|
||||||
case 'domains':
|
case 'domains':
|
||||||
$domains = array();
|
$domains = array();
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
||||||
@@ -4951,9 +4989,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username
|
':username' => $username
|
||||||
));
|
));
|
||||||
$stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as OR `send_as` = :send_as");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username
|
':logged_in_as' => $username,
|
||||||
|
':send_as' => $username
|
||||||
));
|
));
|
||||||
// fk, better safe than sorry
|
// fk, better safe than sorry
|
||||||
$stmt = $pdo->prepare("DELETE FROM `user_acl` WHERE `username` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `user_acl` WHERE `username` = :username");
|
||||||
@@ -5053,12 +5092,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_sogo_static_view($username);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
'msg' => array('mailbox_removed', htmlspecialchars($username))
|
'msg' => array('mailbox_removed', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
break;
|
break;
|
||||||
case 'mailbox_templates':
|
case 'mailbox_templates':
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
@@ -5264,7 +5306,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'mailbox', 'resource')) && getenv('SKIP_SOGO') != "y") {
|
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'resource')) && getenv('SKIP_SOGO') != "y") {
|
||||||
update_sogo_static_view();
|
update_sogo_static_view();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -63,7 +63,7 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
|||||||
unset($_SESSION['index_query_string']);
|
unset($_SESSION['index_query_string']);
|
||||||
if (in_array('mobileconfig', $http_parameters)) {
|
if (in_array('mobileconfig', $http_parameters)) {
|
||||||
if (in_array('only_email', $http_parameters)) {
|
if (in_array('only_email', $http_parameters)) {
|
||||||
header("Location: /mobileconfig.php?email_only");
|
header("Location: /mobileconfig.php?only_email");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
header("Location: /mobileconfig.php");
|
header("Location: /mobileconfig.php");
|
||||||
|
@@ -1,3 +1,13 @@
|
|||||||
|
const LOCALE = undefined;
|
||||||
|
const DATETIME_FORMAT = {
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit"
|
||||||
|
};
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// mailcow alert box generator
|
// mailcow alert box generator
|
||||||
window.mailcow_alert_box = function(message, type) {
|
window.mailcow_alert_box = function(message, type) {
|
||||||
|
@@ -117,8 +117,8 @@ jQuery(function($){
|
|||||||
data: 'tfa_active',
|
data: 'tfa_active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>';
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -126,8 +126,8 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>';
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -260,8 +260,8 @@ jQuery(function($){
|
|||||||
data: 'tfa_active',
|
data: 'tfa_active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>';
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -269,8 +269,8 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>';
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -337,7 +337,7 @@ jQuery(function($){
|
|||||||
data: 'keep_spam',
|
data: 'keep_spam',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function(data, type){
|
render: function(data, type){
|
||||||
return 'yes'==data?'<i class="bi bi-x-lg"></i>':'no'==data&&'<i class="bi bi-check-lg"></i>';
|
return 'yes'==data?'<i class="bi bi-x-lg"><span class="sorting-value">yes</span></i>':'no'==data&&'<i class="bi bi-check-lg"><span class="sorting-value">no</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -414,8 +414,8 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>';
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -492,8 +492,8 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
if(data == 1) return '<i class="bi bi-check-lg"></i>';
|
if(data == 1) return '<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>';
|
||||||
else return '<i class="bi bi-x-lg"></i>';
|
else return '<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -1,13 +1,3 @@
|
|||||||
const LOCALE = undefined;
|
|
||||||
const DATETIME_FORMAT = {
|
|
||||||
year: "numeric",
|
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit"
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Parse seconds ago to date
|
// Parse seconds ago to date
|
||||||
// Get "now" timestamp
|
// Get "now" timestamp
|
||||||
@@ -43,7 +33,7 @@ $(document).ready(function() {
|
|||||||
if (mailcow_info.branch === "master"){
|
if (mailcow_info.branch === "master"){
|
||||||
check_update(mailcow_info.version_tag, mailcow_info.project_url);
|
check_update(mailcow_info.version_tag, mailcow_info.project_url);
|
||||||
}
|
}
|
||||||
$("#maiclow_version").click(function(){
|
$("#mailcow_version").click(function(){
|
||||||
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
|
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -829,13 +819,10 @@ jQuery(function($){
|
|||||||
url: '/api/v1/get/rspamd/actions',
|
url: '/api/v1/get/rspamd/actions',
|
||||||
async: true,
|
async: true,
|
||||||
success: function(data){
|
success: function(data){
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
var total = 0;
|
var total = 0;
|
||||||
$(data).map(function(){total += this[1];});
|
$(data).map(function(){total += this[1];});
|
||||||
var labels = $.makeArray($(data).map(function(){return this[0] + ' ' + Math.round(this[1]/total * 100) + '%';}));
|
var labels = $.makeArray($(data).map(function(){return this[0] + ' ' + Math.round(this[1]/total * 100) + '%';}));
|
||||||
var values = $.makeArray($(data).map(function(){return this[1];}));
|
var values = $.makeArray($(data).map(function(){return this[1];}));
|
||||||
console.log(values);
|
|
||||||
|
|
||||||
var graphdata = {
|
var graphdata = {
|
||||||
labels: labels,
|
labels: labels,
|
||||||
@@ -951,12 +938,15 @@ jQuery(function($){
|
|||||||
title: 'Score',
|
title: 'Score',
|
||||||
data: 'score',
|
data: 'score',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
|
class: 'text-nowrap',
|
||||||
createdCell: function(td, cellData) {
|
createdCell: function(td, cellData) {
|
||||||
$(td).attr({
|
$(td).attr({
|
||||||
"data-order": cellData.sortBy,
|
"data-order": cellData.sortBy,
|
||||||
"data-sort": cellData.sortBy
|
"data-sort": cellData.sortBy
|
||||||
});
|
});
|
||||||
$(td).html(cellData.value);
|
},
|
||||||
|
render: function (data) {
|
||||||
|
return data.value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -979,7 +969,9 @@ jQuery(function($){
|
|||||||
"data-order": cellData.sortBy,
|
"data-order": cellData.sortBy,
|
||||||
"data-sort": cellData.sortBy
|
"data-sort": cellData.sortBy
|
||||||
});
|
});
|
||||||
$(td).html(cellData.value);
|
},
|
||||||
|
render: function (data) {
|
||||||
|
return data.value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1302,6 +1294,12 @@ function update_stats(timeout=5){
|
|||||||
$("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
|
$("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
|
||||||
$("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
|
$("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
|
||||||
$("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
|
$("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
|
||||||
|
if (data.architecture == "aarch64"){
|
||||||
|
$("#host_architecture").html('<span data-bs-toggle="tooltip" data-bs-placement="top" title="' + lang_debug.wip +'">' + data.architecture + ' ⚠️</span>');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$("#host_architecture").html(data.architecture);
|
||||||
|
}
|
||||||
|
|
||||||
// update cpu and mem chart
|
// update cpu and mem chart
|
||||||
var cpu_chart = Chart.getChart("host_cpu_chart");
|
var cpu_chart = Chart.getChart("host_cpu_chart");
|
||||||
|
@@ -607,7 +607,7 @@ jQuery(function($){
|
|||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
responsivePriority: 6,
|
responsivePriority: 6,
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—');
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -754,7 +754,7 @@ jQuery(function($){
|
|||||||
data: 'attributes.gal',
|
data: 'attributes.gal',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -762,7 +762,7 @@ jQuery(function($){
|
|||||||
data: 'attributes.backupmx',
|
data: 'attributes.backupmx',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -770,7 +770,7 @@ jQuery(function($){
|
|||||||
data: 'attributes.relay_all_recipients',
|
data: 'attributes.relay_all_recipients',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -778,7 +778,7 @@ jQuery(function($){
|
|||||||
data: 'attributes.relay_unknown_only',
|
data: 'attributes.relay_unknown_only',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -787,7 +787,7 @@ jQuery(function($){
|
|||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
responsivePriority: 4,
|
responsivePriority: 4,
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1093,7 +1093,7 @@ jQuery(function($){
|
|||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
responsivePriority: 4,
|
responsivePriority: 4,
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—');
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1164,13 +1164,13 @@ jQuery(function($){
|
|||||||
|
|
||||||
item.attributes.quota = humanFileSize(item.attributes.quota);
|
item.attributes.quota = humanFileSize(item.attributes.quota);
|
||||||
|
|
||||||
item.attributes.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>';
|
item.attributes.tls_enforce_in = '<i class="text-' + (item.attributes.tls_enforce_in == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_in == 1 ? '1' : '0') + '</span></i>';
|
||||||
item.attributes.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"></i>';
|
item.attributes.tls_enforce_out = '<i class="text-' + (item.attributes.tls_enforce_out == 1 ? 'success bi bi-lock-fill' : 'danger bi bi-unlock-fill') + '"><span class="sorting-value">' + (item.attributes.tls_enforce_out == 1 ? '1' : '0') + '</span></i>';
|
||||||
item.attributes.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
|
item.attributes.pop3_access = '<i class="text-' + (item.attributes.pop3_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.pop3_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.pop3_access == 1 ? '1' : '0') + '</span></i>';
|
||||||
item.attributes.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
|
item.attributes.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.imap_access == 1 ? '1' : '0') + '</span></i>';
|
||||||
item.attributes.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
|
item.attributes.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.smtp_access == 1 ? '1' : '0') + '</span></i>';
|
||||||
item.attributes.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
|
item.attributes.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sieve_access == 1 ? '1' : '0') + '</span></i>';
|
||||||
item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
|
item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sogo_access == 1 ? '1' : '0') + '</span></i>';
|
||||||
if (item.attributes.quarantine_notification === 'never') {
|
if (item.attributes.quarantine_notification === 'never') {
|
||||||
item.attributes.quarantine_notification = lang.never;
|
item.attributes.quarantine_notification = lang.never;
|
||||||
} else if (item.attributes.quarantine_notification === 'hourly') {
|
} else if (item.attributes.quarantine_notification === 'hourly') {
|
||||||
@@ -1188,7 +1188,6 @@ jQuery(function($){
|
|||||||
item.attributes.quarantine_category = lang.q_all;
|
item.attributes.quarantine_category = lang.q_all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (item.template.toLowerCase() == "default"){
|
if (item.template.toLowerCase() == "default"){
|
||||||
item.action = '<div class="btn-group">' +
|
item.action = '<div class="btn-group">' +
|
||||||
'<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
|
'<a href="/edit/template/' + encodeURIComponent(item.id) + '" class="btn btn-xs btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
|
||||||
@@ -1329,7 +1328,7 @@ jQuery(function($){
|
|||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
responsivePriority: 4,
|
responsivePriority: 4,
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—');
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1440,7 +1439,7 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—');
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1459,30 +1458,37 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
function draw_bcc_table() {
|
function draw_bcc_table() {
|
||||||
$.get("/api/v1/get/bcc-destination-options", function(data){
|
$.get("/api/v1/get/bcc-destination-options", function(data){
|
||||||
|
var optgroup = "";
|
||||||
// Domains
|
// Domains
|
||||||
var optgroup = "<optgroup label='" + lang.domains + "'>";
|
if (data.domains && data.domains.length > 0) {
|
||||||
$.each(data.domains, function(index, domain){
|
optgroup = "<optgroup label='" + lang.domains + "'>";
|
||||||
optgroup += "<option value='" + domain + "'>" + domain + "</option>";
|
$.each(data.domains, function(index, domain){
|
||||||
});
|
optgroup += "<option value='" + domain + "'>" + domain + "</option>";
|
||||||
optgroup += "</optgroup>";
|
|
||||||
$('#bcc-local-dest').append(optgroup);
|
|
||||||
// Alias domains
|
|
||||||
var optgroup = "<optgroup label='" + lang.domain_aliases + "'>";
|
|
||||||
$.each(data.alias_domains, function(index, alias_domain){
|
|
||||||
optgroup += "<option value='" + alias_domain + "'>" + alias_domain + "</option>";
|
|
||||||
});
|
|
||||||
optgroup += "</optgroup>"
|
|
||||||
$('#bcc-local-dest').append(optgroup);
|
|
||||||
// Mailboxes and aliases
|
|
||||||
$.each(data.mailboxes, function(mailbox, aliases){
|
|
||||||
var optgroup = "<optgroup label='" + mailbox + "'>";
|
|
||||||
$.each(aliases, function(index, alias){
|
|
||||||
optgroup += "<option value='" + alias + "'>" + alias + "</option>";
|
|
||||||
});
|
});
|
||||||
optgroup += "</optgroup>";
|
optgroup += "</optgroup>";
|
||||||
$('#bcc-local-dest').append(optgroup);
|
$('#bcc-local-dest').append(optgroup);
|
||||||
});
|
}
|
||||||
// Finish
|
// Alias domains
|
||||||
|
if (data.alias_domains && data.alias_domains.length > 0) {
|
||||||
|
optgroup = "<optgroup label='" + lang.domain_aliases + "'>";
|
||||||
|
$.each(data.alias_domains, function(index, alias_domain){
|
||||||
|
optgroup += "<option value='" + alias_domain + "'>" + alias_domain + "</option>";
|
||||||
|
});
|
||||||
|
optgroup += "</optgroup>"
|
||||||
|
$('#bcc-local-dest').append(optgroup);
|
||||||
|
}
|
||||||
|
// Mailboxes and aliases
|
||||||
|
if (data.mailboxes && Object.keys(data.mailboxes).length > 0) {
|
||||||
|
$.each(data.mailboxes, function(mailbox, aliases){
|
||||||
|
optgroup = "<optgroup label='" + mailbox + "'>";
|
||||||
|
$.each(aliases, function(index, alias){
|
||||||
|
optgroup += "<option value='" + alias + "'>" + alias + "</option>";
|
||||||
|
});
|
||||||
|
optgroup += "</optgroup>";
|
||||||
|
$('#bcc-local-dest').append(optgroup);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Recreate picker
|
||||||
$('#bcc-local-dest').selectpicker('refresh');
|
$('#bcc-local-dest').selectpicker('refresh');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1578,7 +1584,7 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':(0==data?'<i class="bi bi-x-lg"></i>':2==data&&'—');
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':(0==data?'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>':2==data&&'—');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1675,7 +1681,7 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1782,7 +1788,7 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1917,7 +1923,7 @@ jQuery(function($){
|
|||||||
data: 'sogo_visible',
|
data: 'sogo_visible',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function(data, type){
|
render: function(data, type){
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1936,7 +1942,7 @@ jQuery(function($){
|
|||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
responsivePriority: 6,
|
responsivePriority: 6,
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1952,6 +1958,10 @@ jQuery(function($){
|
|||||||
table.on('responsive-resize', function (e, datatable, columns){
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table');
|
hideTableExpandCollapseBtn('#tab-mbox-aliases', '#alias_table');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
table.on( 'draw', function (){
|
||||||
|
$('#alias_table [data-bs-toggle="tooltip"]').tooltip();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function draw_aliasdomain_table() {
|
function draw_aliasdomain_table() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
@@ -2031,7 +2041,7 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2167,7 +2177,7 @@ jQuery(function($){
|
|||||||
data: 'active',
|
data: 'active',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
render: function (data, type) {
|
||||||
return 1==data?'<i class="bi bi-check-lg"></i>':0==data&&'<i class="bi bi-x-lg"></i>';
|
return 1==data?'<i class="bi bi-check-lg"><span class="sorting-value">1</span></i>':0==data&&'<i class="bi bi-x-lg"><span class="sorting-value">0</span></i>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2323,16 +2333,19 @@ jQuery(function($){
|
|||||||
// detect element visibility changes
|
// detect element visibility changes
|
||||||
function onVisible(element, callback) {
|
function onVisible(element, callback) {
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
element_object = document.querySelector(element);
|
let element_object = document.querySelector(element);
|
||||||
if (element_object === null) return;
|
if (element_object === null) return;
|
||||||
|
|
||||||
new IntersectionObserver((entries, observer) => {
|
let observer = new IntersectionObserver((entries, observer) => {
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
if(entry.intersectionRatio > 0) {
|
if(entry.intersectionRatio > 0) {
|
||||||
callback(element_object);
|
callback(element_object);
|
||||||
|
observer.unobserve(element_object);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).observe(element_object);
|
})
|
||||||
|
|
||||||
|
observer.observe(element_object);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -127,6 +127,20 @@ jQuery(function($){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createSortableDate(td, cellData, date_string = false) {
|
||||||
|
if (date_string)
|
||||||
|
var date = new Date(cellData);
|
||||||
|
else
|
||||||
|
var date = new Date(cellData ? cellData * 1000 : 0);
|
||||||
|
|
||||||
|
var timestamp = date.getTime();
|
||||||
|
$(td).attr({
|
||||||
|
"data-order": timestamp,
|
||||||
|
"data-sort": timestamp
|
||||||
|
});
|
||||||
|
$(td).html(date.toLocaleDateString(LOCALE, DATETIME_FORMAT));
|
||||||
|
}
|
||||||
function draw_tla_table() {
|
function draw_tla_table() {
|
||||||
// just recalc width if instance already exists
|
// just recalc width if instance already exists
|
||||||
if ($.fn.DataTable.isDataTable('#tla_table') ) {
|
if ($.fn.DataTable.isDataTable('#tla_table') ) {
|
||||||
@@ -144,6 +158,7 @@ jQuery(function($){
|
|||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
language: lang_datatables,
|
language: lang_datatables,
|
||||||
|
order: [[4, 'desc']],
|
||||||
ajax: {
|
ajax: {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/time_limited_aliases",
|
url: "/api/v1/get/time_limited_aliases",
|
||||||
@@ -191,18 +206,16 @@ jQuery(function($){
|
|||||||
title: lang.alias_valid_until,
|
title: lang.alias_valid_until,
|
||||||
data: 'validity',
|
data: 'validity',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
createdCell: function(td, cellData) {
|
||||||
var date = new Date(data ? data * 1000 : 0);
|
createSortableDate(td, cellData)
|
||||||
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.created_on,
|
title: lang.created_on,
|
||||||
data: 'created',
|
data: 'created',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type) {
|
createdCell: function(td, cellData) {
|
||||||
var date = new Date(data.replace(/-/g, "/"));
|
createSortableDate(td, cellData, true)
|
||||||
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -105,7 +105,8 @@
|
|||||||
"timeout2": "Časový limit pro připojení k lokálnímu serveru",
|
"timeout2": "Časový limit pro připojení k lokálnímu serveru",
|
||||||
"username": "Uživatelské jméno",
|
"username": "Uživatelské jméno",
|
||||||
"validate": "Ověřit",
|
"validate": "Ověřit",
|
||||||
"validation_success": "Úspěšně ověřeno"
|
"validation_success": "Úspěšně ověřeno",
|
||||||
|
"tags": "Štítky"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Přístupy",
|
"access": "Přístupy",
|
||||||
@@ -333,7 +334,11 @@
|
|||||||
"username": "Uživatelské jméno",
|
"username": "Uživatelské jméno",
|
||||||
"validate_license_now": "Ověřit GUID na licenčním serveru",
|
"validate_license_now": "Ověřit GUID na licenčním serveru",
|
||||||
"verify": "Ověřit",
|
"verify": "Ověřit",
|
||||||
"yes": "✓"
|
"yes": "✓",
|
||||||
|
"f2b_ban_time_increment": "Délka banu je prodlužována s každým dalším banem",
|
||||||
|
"f2b_max_ban_time": "Maximální délka banu (s)",
|
||||||
|
"ip_check": "Kontrola IP",
|
||||||
|
"ip_check_disabled": "Kontrola IP je vypnuta. Můžete ji zapnout v <br> <strong>System > Nastavení > Options > Přizpůsobení</strong>"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři",
|
"access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři",
|
||||||
@@ -536,7 +541,7 @@
|
|||||||
"inactive": "Neaktivní",
|
"inactive": "Neaktivní",
|
||||||
"kind": "Druh",
|
"kind": "Druh",
|
||||||
"last_modified": "Naposledy změněn",
|
"last_modified": "Naposledy změněn",
|
||||||
"lookup_mx": "Cíl je regulární výraz který se shoduje s MX záznamem (<code>.*google\\.com</code> směřuje veškerou poštu na MX které jsou cílem pro google.com přes tento skok)",
|
"lookup_mx": "Cíl je regulární výraz který se shoduje s MX záznamem (<code>.*\\.google\\.com</code> směřuje veškerou poštu na MX které jsou cílem pro google.com přes tento skok)",
|
||||||
"mailbox": "Úprava mailové schránky",
|
"mailbox": "Úprava mailové schránky",
|
||||||
"mailbox_quota_def": "Výchozí kvóta schránky",
|
"mailbox_quota_def": "Výchozí kvóta schránky",
|
||||||
"mailbox_relayhost_info": "Aplikované jen na uživatelskou schránku a přímé aliasy, přepisuje předávající server domény.",
|
"mailbox_relayhost_info": "Aplikované jen na uživatelskou schránku a přímé aliasy, přepisuje předávající server domény.",
|
||||||
|
@@ -175,10 +175,12 @@
|
|||||||
"empty": "Keine Einträge vorhanden",
|
"empty": "Keine Einträge vorhanden",
|
||||||
"excludes": "Diese Empfänger ausschließen",
|
"excludes": "Diese Empfänger ausschließen",
|
||||||
"f2b_ban_time": "Bannzeit in Sekunden",
|
"f2b_ban_time": "Bannzeit in Sekunden",
|
||||||
|
"f2b_ban_time_increment": "Bannzeit erhöht sich mit jedem Bann",
|
||||||
"f2b_blacklist": "Blacklist für Netzwerke und Hosts",
|
"f2b_blacklist": "Blacklist für Netzwerke und Hosts",
|
||||||
"f2b_filter": "Regex-Filter",
|
"f2b_filter": "Regex-Filter",
|
||||||
"f2b_list_info": "Ein Host oder Netzwerk auf der Blacklist wird immer eine Whitelist-Einheit überwiegen. <b>Die Aktualisierung der Liste dauert einige Sekunden.</b>",
|
"f2b_list_info": "Ein Host oder Netzwerk auf der Blacklist wird immer eine Whitelist-Einheit überwiegen. <b>Die Aktualisierung der Liste dauert einige Sekunden.</b>",
|
||||||
"f2b_max_attempts": "Max. Versuche",
|
"f2b_max_attempts": "Max. Versuche",
|
||||||
|
"f2b_max_ban_time": "Maximale Bannzeit in Sekunden",
|
||||||
"f2b_netban_ipv4": "Netzbereich für IPv4-Banns (8-32)",
|
"f2b_netban_ipv4": "Netzbereich für IPv4-Banns (8-32)",
|
||||||
"f2b_netban_ipv6": "Netzbereich für IPv6-Banns (8-128)",
|
"f2b_netban_ipv6": "Netzbereich für IPv6-Banns (8-128)",
|
||||||
"f2b_parameters": "Fail2ban-Parameter",
|
"f2b_parameters": "Fail2ban-Parameter",
|
||||||
@@ -214,7 +216,7 @@
|
|||||||
"loading": "Bitte warten...",
|
"loading": "Bitte warten...",
|
||||||
"login_time": "Zeit",
|
"login_time": "Zeit",
|
||||||
"logo_info": "Die hochgeladene Grafik wird für die Navigationsleiste auf eine Höhe von 40px skaliert. Für die Darstellung auf der Login-Maske beträgt die skalierte Breite maximal 250px. Eine frei skalierbare Grafik (etwa SVG) wird empfohlen.",
|
"logo_info": "Die hochgeladene Grafik wird für die Navigationsleiste auf eine Höhe von 40px skaliert. Für die Darstellung auf der Login-Maske beträgt die skalierte Breite maximal 250px. Eine frei skalierbare Grafik (etwa SVG) wird empfohlen.",
|
||||||
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*\\.google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
||||||
"main_name": "\"mailcow UI\" Name",
|
"main_name": "\"mailcow UI\" Name",
|
||||||
"merged_vars_hint": "Ausgegraute Reihen wurden aus der Datei <code>vars.(local.)inc.php</code> gelesen und können hier nicht verändert werden.",
|
"merged_vars_hint": "Ausgegraute Reihen wurden aus der Datei <code>vars.(local.)inc.php</code> gelesen und können hier nicht verändert werden.",
|
||||||
"message": "Nachricht",
|
"message": "Nachricht",
|
||||||
@@ -496,6 +498,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
|
"architecture": "Architektur",
|
||||||
"chart_this_server": "Chart (dieser Server)",
|
"chart_this_server": "Chart (dieser Server)",
|
||||||
"containers_info": "Container-Information",
|
"containers_info": "Container-Information",
|
||||||
"container_running": "Läuft",
|
"container_running": "Läuft",
|
||||||
@@ -532,7 +535,8 @@
|
|||||||
"update_available": "Es ist ein Update verfügbar",
|
"update_available": "Es ist ein Update verfügbar",
|
||||||
"no_update_available": "Das System ist auf aktuellem Stand",
|
"no_update_available": "Das System ist auf aktuellem Stand",
|
||||||
"update_failed": "Es konnte nicht nach einem Update gesucht werden",
|
"update_failed": "Es konnte nicht nach einem Update gesucht werden",
|
||||||
"username": "Benutzername"
|
"username": "Benutzername",
|
||||||
|
"wip": "Aktuell noch in Arbeit"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"cname_from_a": "Wert abgeleitet von A/AAAA-Eintrag. Wird unterstützt, sofern der Eintrag auf die korrekte Ressource zeigt.",
|
"cname_from_a": "Wert abgeleitet von A/AAAA-Eintrag. Wird unterstützt, sofern der Eintrag auf die korrekte Ressource zeigt.",
|
||||||
@@ -591,7 +595,7 @@
|
|||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"kind": "Art",
|
"kind": "Art",
|
||||||
"last_modified": "Zuletzt geändert",
|
"last_modified": "Zuletzt geändert",
|
||||||
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*\\.google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
||||||
"mailbox": "Mailbox bearbeiten",
|
"mailbox": "Mailbox bearbeiten",
|
||||||
"mailbox_quota_def": "Standard-Quota einer Mailbox",
|
"mailbox_quota_def": "Standard-Quota einer Mailbox",
|
||||||
"mailbox_relayhost_info": "Wird auf eine Mailbox und direkte Alias-Adressen angewendet. Überschreibt die Einstellung einer Domain.",
|
"mailbox_relayhost_info": "Wird auf eine Mailbox und direkte Alias-Adressen angewendet. Überschreibt die Einstellung einer Domain.",
|
||||||
|
@@ -177,10 +177,12 @@
|
|||||||
"empty": "No results",
|
"empty": "No results",
|
||||||
"excludes": "Excludes these recipients",
|
"excludes": "Excludes these recipients",
|
||||||
"f2b_ban_time": "Ban time (s)",
|
"f2b_ban_time": "Ban time (s)",
|
||||||
|
"f2b_ban_time_increment": "Ban time is incremented with each ban",
|
||||||
"f2b_blacklist": "Blacklisted networks/hosts",
|
"f2b_blacklist": "Blacklisted networks/hosts",
|
||||||
"f2b_filter": "Regex filters",
|
"f2b_filter": "Regex filters",
|
||||||
"f2b_list_info": "A blacklisted host or network will always outweigh a whitelist entity. <b>List updates will take a few seconds to be applied.</b>",
|
"f2b_list_info": "A blacklisted host or network will always outweigh a whitelist entity. <b>List updates will take a few seconds to be applied.</b>",
|
||||||
"f2b_max_attempts": "Max. attempts",
|
"f2b_max_attempts": "Max. attempts",
|
||||||
|
"f2b_max_ban_time": "Max. ban time (s)",
|
||||||
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
|
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
|
||||||
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
|
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
|
||||||
"f2b_parameters": "Fail2ban parameters",
|
"f2b_parameters": "Fail2ban parameters",
|
||||||
@@ -216,7 +218,7 @@
|
|||||||
"loading": "Please wait...",
|
"loading": "Please wait...",
|
||||||
"login_time": "Login time",
|
"login_time": "Login time",
|
||||||
"logo_info": "Your image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. A scalable graphic is highly recommended.",
|
"logo_info": "Your image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. A scalable graphic is highly recommended.",
|
||||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||||
"main_name": "\"mailcow UI\" name",
|
"main_name": "\"mailcow UI\" name",
|
||||||
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
|
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
|
||||||
"message": "Message",
|
"message": "Message",
|
||||||
@@ -496,6 +498,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
|
"architecture": "Architecture",
|
||||||
"chart_this_server": "Chart (this server)",
|
"chart_this_server": "Chart (this server)",
|
||||||
"containers_info": "Container information",
|
"containers_info": "Container information",
|
||||||
"container_running": "Running",
|
"container_running": "Running",
|
||||||
@@ -532,7 +535,8 @@
|
|||||||
"update_available": "There is an update available",
|
"update_available": "There is an update available",
|
||||||
"no_update_available": "The System is on the latest version",
|
"no_update_available": "The System is on the latest version",
|
||||||
"update_failed": "Could not check for an Update",
|
"update_failed": "Could not check for an Update",
|
||||||
"username": "Username"
|
"username": "Username",
|
||||||
|
"wip": "Currently Work in Progress"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"cname_from_a": "Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.",
|
"cname_from_a": "Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.",
|
||||||
@@ -591,7 +595,7 @@
|
|||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
"kind": "Kind",
|
"kind": "Kind",
|
||||||
"last_modified": "Last modified",
|
"last_modified": "Last modified",
|
||||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||||
"mailbox": "Edit mailbox",
|
"mailbox": "Edit mailbox",
|
||||||
"mailbox_quota_def": "Default mailbox quota",
|
"mailbox_quota_def": "Default mailbox quota",
|
||||||
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
||||||
|
@@ -141,9 +141,11 @@
|
|||||||
"empty": "Sin resultados",
|
"empty": "Sin resultados",
|
||||||
"excludes": "Excluye a estos destinatarios",
|
"excludes": "Excluye a estos destinatarios",
|
||||||
"f2b_ban_time": "Tiempo de restricción (s)",
|
"f2b_ban_time": "Tiempo de restricción (s)",
|
||||||
|
"f2b_ban_time_increment": "Tiempo de restricción se incrementa con cada restricción",
|
||||||
"f2b_blacklist": "Redes y hosts en lista negra",
|
"f2b_blacklist": "Redes y hosts en lista negra",
|
||||||
"f2b_list_info": "Un host o red en lista negra siempre superará a una entidad de la lista blanca. <b>Las actualizaciones de la lista tardarán unos segundos en aplicarse.</b>",
|
"f2b_list_info": "Un host o red en lista negra siempre superará a una entidad de la lista blanca. <b>Las actualizaciones de la lista tardarán unos segundos en aplicarse.</b>",
|
||||||
"f2b_max_attempts": "Max num. de intentos",
|
"f2b_max_attempts": "Max num. de intentos",
|
||||||
|
"f2b_max_ban_time": "Max tiempo de restricción (s)",
|
||||||
"f2b_netban_ipv4": "Tamaño de subred IPv4 para aplicar la restricción (8-32)",
|
"f2b_netban_ipv4": "Tamaño de subred IPv4 para aplicar la restricción (8-32)",
|
||||||
"f2b_netban_ipv6": "Tamaño de subred IPv6 para aplicar la restricción (8-128)",
|
"f2b_netban_ipv6": "Tamaño de subred IPv6 para aplicar la restricción (8-128)",
|
||||||
"f2b_parameters": "Parametros Fail2ban",
|
"f2b_parameters": "Parametros Fail2ban",
|
||||||
|
@@ -172,11 +172,13 @@
|
|||||||
"edit": "Editer",
|
"edit": "Editer",
|
||||||
"empty": "Aucun résultat",
|
"empty": "Aucun résultat",
|
||||||
"excludes": "Exclure ces destinataires",
|
"excludes": "Exclure ces destinataires",
|
||||||
"f2b_ban_time": "Durée du bannissement(s)",
|
"f2b_ban_time": "Durée du bannissement (s)",
|
||||||
|
"f2b_ban_time_increment": "Durée du bannissement est augmentée à chaque bannissement",
|
||||||
"f2b_blacklist": "Réseaux/Domaines sur Liste Noire",
|
"f2b_blacklist": "Réseaux/Domaines sur Liste Noire",
|
||||||
"f2b_filter": "Filtre(s) Regex",
|
"f2b_filter": "Filtre(s) Regex",
|
||||||
"f2b_list_info": "Un hôte ou un réseau sur liste noire l'emportera toujours sur une entité de liste blanche. <b>L'application des mises à jour de liste prendra quelques secondes.</b>",
|
"f2b_list_info": "Un hôte ou un réseau sur liste noire l'emportera toujours sur une entité de liste blanche. <b>L'application des mises à jour de liste prendra quelques secondes.</b>",
|
||||||
"f2b_max_attempts": "Nb max. de tentatives",
|
"f2b_max_attempts": "Nb max. de tentatives",
|
||||||
|
"f2b_max_ban_time": "Max. durée du bannissement (s)",
|
||||||
"f2b_netban_ipv4": "Taille du sous-réseau IPv4 pour l'application du bannissement (8-32)",
|
"f2b_netban_ipv4": "Taille du sous-réseau IPv4 pour l'application du bannissement (8-32)",
|
||||||
"f2b_netban_ipv6": "Taille du sous-réseau IPv6 pour l'application du bannissement (8-128)",
|
"f2b_netban_ipv6": "Taille du sous-réseau IPv6 pour l'application du bannissement (8-128)",
|
||||||
"f2b_parameters": "Paramètres Fail2ban",
|
"f2b_parameters": "Paramètres Fail2ban",
|
||||||
@@ -586,7 +588,7 @@
|
|||||||
"unchanged_if_empty": "Si non modifié, laisser en blanc",
|
"unchanged_if_empty": "Si non modifié, laisser en blanc",
|
||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"validate_save": "Valider et sauver",
|
"validate_save": "Valider et sauver",
|
||||||
"lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut)",
|
"lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*\\.google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut)",
|
||||||
"mailbox_relayhost_info": "S'applique uniquement à la boîte aux lettres et aux alias directs, remplace le relayhost du domaine."
|
"mailbox_relayhost_info": "S'applique uniquement à la boîte aux lettres et aux alias directs, remplace le relayhost du domaine."
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
|
@@ -175,10 +175,12 @@
|
|||||||
"empty": "Nessun risultato",
|
"empty": "Nessun risultato",
|
||||||
"excludes": "Esclude questi destinatari",
|
"excludes": "Esclude questi destinatari",
|
||||||
"f2b_ban_time": "Tempo di blocco (s)",
|
"f2b_ban_time": "Tempo di blocco (s)",
|
||||||
|
"f2b_ban_time_increment": "Tempo di blocco aumenta ad ogni blocco",
|
||||||
"f2b_blacklist": "Host/reti in blacklist",
|
"f2b_blacklist": "Host/reti in blacklist",
|
||||||
"f2b_filter": "Filtri Regex",
|
"f2b_filter": "Filtri Regex",
|
||||||
"f2b_list_info": "Un host oppure una rete in blacklist, avrà sempre un peso maggiore rispetto ad una in whitelist. <b>L'aggiornamento della lista richiede alcuni secondi per la sua entrata in azione.</b>",
|
"f2b_list_info": "Un host oppure una rete in blacklist, avrà sempre un peso maggiore rispetto ad una in whitelist. <b>L'aggiornamento della lista richiede alcuni secondi per la sua entrata in azione.</b>",
|
||||||
"f2b_max_attempts": "Tentativi massimi",
|
"f2b_max_attempts": "Tentativi massimi",
|
||||||
|
"f2b_max_ban_time": "Tempo massimo di blocco (s)",
|
||||||
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
|
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
|
||||||
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
|
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
|
||||||
"f2b_parameters": "Parametri Fail2ban",
|
"f2b_parameters": "Parametri Fail2ban",
|
||||||
@@ -211,7 +213,7 @@
|
|||||||
"loading": "Caricamento in corso...",
|
"loading": "Caricamento in corso...",
|
||||||
"login_time": "Ora di accesso",
|
"login_time": "Ora di accesso",
|
||||||
"logo_info": "La tua immagine verrà ridimensionata a 40px di altezza, quando verrà usata nella barra di navigazione in alto, ed ad una larghezza massima di 250px nella schermata iniziale. È altamente consigliato l'utilizzo di un'immagine modulabile.",
|
"logo_info": "La tua immagine verrà ridimensionata a 40px di altezza, quando verrà usata nella barra di navigazione in alto, ed ad una larghezza massima di 250px nella schermata iniziale. È altamente consigliato l'utilizzo di un'immagine modulabile.",
|
||||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||||
"main_name": "Nome \"mailcow UI\"",
|
"main_name": "Nome \"mailcow UI\"",
|
||||||
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
|
"merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
|
||||||
"message": "Messaggio",
|
"message": "Messaggio",
|
||||||
@@ -552,7 +554,7 @@
|
|||||||
"hostname": "Hostname",
|
"hostname": "Hostname",
|
||||||
"inactive": "Inattivo",
|
"inactive": "Inattivo",
|
||||||
"kind": "Genere",
|
"kind": "Genere",
|
||||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||||
"mailbox": "Modifica casella di posta",
|
"mailbox": "Modifica casella di posta",
|
||||||
"mailbox_quota_def": "Default mailbox quota",
|
"mailbox_quota_def": "Default mailbox quota",
|
||||||
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
||||||
|
@@ -168,10 +168,12 @@
|
|||||||
"empty": "Geen resultaten",
|
"empty": "Geen resultaten",
|
||||||
"excludes": "Exclusief",
|
"excludes": "Exclusief",
|
||||||
"f2b_ban_time": "Verbanningstijd (s)",
|
"f2b_ban_time": "Verbanningstijd (s)",
|
||||||
|
"f2b_ban_time_increment": "Verbanningstijd wordt verhoogd met elk verbanning",
|
||||||
"f2b_blacklist": "Netwerken/hosts op de blacklist",
|
"f2b_blacklist": "Netwerken/hosts op de blacklist",
|
||||||
"f2b_filter": "Regex-filters",
|
"f2b_filter": "Regex-filters",
|
||||||
"f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. <b>Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.</b>",
|
"f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. <b>Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.</b>",
|
||||||
"f2b_max_attempts": "Maximaal aantal pogingen",
|
"f2b_max_attempts": "Maximaal aantal pogingen",
|
||||||
|
"f2b_max_ban_time": "Maximaal verbanningstijd (s)",
|
||||||
"f2b_netban_ipv4": "Voer de IPv4-subnetgrootte in waar de verbanning van kracht moet zijn (8-32)",
|
"f2b_netban_ipv4": "Voer de IPv4-subnetgrootte in waar de verbanning van kracht moet zijn (8-32)",
|
||||||
"f2b_netban_ipv6": "Voer de IPv6-subnetgrootte in waar de verbanning van kracht moet zijn (8-128)",
|
"f2b_netban_ipv6": "Voer de IPv6-subnetgrootte in waar de verbanning van kracht moet zijn (8-128)",
|
||||||
"f2b_parameters": "Fail2ban",
|
"f2b_parameters": "Fail2ban",
|
||||||
|
@@ -539,7 +539,7 @@
|
|||||||
"inactive": "Inactiv",
|
"inactive": "Inactiv",
|
||||||
"kind": "Fel",
|
"kind": "Fel",
|
||||||
"last_modified": "Ultima modificare",
|
"last_modified": "Ultima modificare",
|
||||||
"lookup_mx": "Destinația este o expresie regulată care potrivită cu numele MX (<code>.*google\\.com</code> pentru a direcționa toate e-mailurile vizate către un MX care se termină în google.com peste acest hop)",
|
"lookup_mx": "Destinația este o expresie regulată care potrivită cu numele MX (<code>.*\\.google\\.com</code> pentru a direcționa toate e-mailurile vizate către un MX care se termină în google.com peste acest hop)",
|
||||||
"mailbox": "Editează căsuța poștală",
|
"mailbox": "Editează căsuța poștală",
|
||||||
"mailbox_quota_def": "Cota implicită a căsuței poștale",
|
"mailbox_quota_def": "Cota implicită a căsuței poștale",
|
||||||
"mailbox_relayhost_info": "Aplicat numai căsuței poștale și aliasurilor directe, suprascrie un transport dependent de domeniu.",
|
"mailbox_relayhost_info": "Aplicat numai căsuței poștale și aliasurilor directe, suprascrie un transport dependent de domeniu.",
|
||||||
|
@@ -336,7 +336,9 @@
|
|||||||
"validate_license_now": "Получить лицензию на основе GUID с сервера лицензий",
|
"validate_license_now": "Получить лицензию на основе GUID с сервера лицензий",
|
||||||
"verify": "Проверить",
|
"verify": "Проверить",
|
||||||
"yes": "✓",
|
"yes": "✓",
|
||||||
"queue_unban": "разблокировать"
|
"queue_unban": "разблокировать",
|
||||||
|
"f2b_ban_time_increment": "Время бана увеличивается с каждым баном",
|
||||||
|
"f2b_max_ban_time": "Максимальное время блокировки"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Доступ запрещён, или указаны неверные данные",
|
"access_denied": "Доступ запрещён, или указаны неверные данные",
|
||||||
|
@@ -213,7 +213,7 @@
|
|||||||
"loading": "Čakajte prosím ...",
|
"loading": "Čakajte prosím ...",
|
||||||
"login_time": "Čas prihlásenia",
|
"login_time": "Čas prihlásenia",
|
||||||
"logo_info": "Váš obrázok bude upravený na výšku 40px pre vrchný navigačný riadok a na maximálnu šírku 250px pre úvodnú stránku. Odporúča sa škálovateľná grafika.",
|
"logo_info": "Váš obrázok bude upravený na výšku 40px pre vrchný navigačný riadok a na maximálnu šírku 250px pre úvodnú stránku. Odporúča sa škálovateľná grafika.",
|
||||||
"lookup_mx": "Cieľ je regulárny výraz ktorý sa porovnáva s MX záznamom (<code>.*google\\.com</code> smeruje všetku poštu určenú pre MX ktoré sú cieľom pre google.com cez tento skok)",
|
"lookup_mx": "Cieľ je regulárny výraz ktorý sa porovnáva s MX záznamom (<code>.*\\.google\\.com</code> smeruje všetku poštu určenú pre MX ktoré sú cieľom pre google.com cez tento skok)",
|
||||||
"main_name": "\"mailcow UI\" názov",
|
"main_name": "\"mailcow UI\" názov",
|
||||||
"merged_vars_hint": "Sivé riadky boli načítané z <code>vars.(local.)inc.php</code> a nemôžu byť modifikované cez UI.",
|
"merged_vars_hint": "Sivé riadky boli načítané z <code>vars.(local.)inc.php</code> a nemôžu byť modifikované cez UI.",
|
||||||
"message": "Správa",
|
"message": "Správa",
|
||||||
@@ -539,7 +539,7 @@
|
|||||||
"inactive": "Neaktívny",
|
"inactive": "Neaktívny",
|
||||||
"kind": "Druh",
|
"kind": "Druh",
|
||||||
"last_modified": "Naposledy upravené",
|
"last_modified": "Naposledy upravené",
|
||||||
"lookup_mx": "Cieľ je regulárny výraz ktorý sa zhoduje s MX záznamom (<code>.*google\\.com</code> smeruje všetku poštu na MX ktoré sú cieľom pre google.com cez tento skok)",
|
"lookup_mx": "Cieľ je regulárny výraz ktorý sa zhoduje s MX záznamom (<code>.*\\.google\\.com</code> smeruje všetku poštu na MX ktoré sú cieľom pre google.com cez tento skok)",
|
||||||
"mailbox": "Upraviť mailovú schránku",
|
"mailbox": "Upraviť mailovú schránku",
|
||||||
"mailbox_quota_def": "Predvolená veľkosť mailovej schránky",
|
"mailbox_quota_def": "Predvolená veľkosť mailovej schránky",
|
||||||
"mailbox_relayhost_info": "Aplikované len na používateľské schránky a priame aliasy, prepisuje doménového preposielateľa.",
|
"mailbox_relayhost_info": "Aplikované len na používateľské schránky a priame aliasy, prepisuje doménového preposielateľa.",
|
||||||
|
@@ -213,7 +213,7 @@
|
|||||||
"loading": "请等待...",
|
"loading": "请等待...",
|
||||||
"login_time": "登录时间",
|
"login_time": "登录时间",
|
||||||
"logo_info": "你的图片将会在顶部导航栏被缩放为 40px 高,在起始页被缩放为最大 250px 高。强烈推荐使用能较好适应缩放的图片。",
|
"logo_info": "你的图片将会在顶部导航栏被缩放为 40px 高,在起始页被缩放为最大 250px 高。强烈推荐使用能较好适应缩放的图片。",
|
||||||
"lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
|
"lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*\\.google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
|
||||||
"main_name": "Mailcow UI 的名称",
|
"main_name": "Mailcow UI 的名称",
|
||||||
"merged_vars_hint": "灰色行来自 <code>vars.(local.)inc.php</code> 文件并且无法修改。",
|
"merged_vars_hint": "灰色行来自 <code>vars.(local.)inc.php</code> 文件并且无法修改。",
|
||||||
"message": "消息",
|
"message": "消息",
|
||||||
@@ -544,7 +544,7 @@
|
|||||||
"hostname": "主机名",
|
"hostname": "主机名",
|
||||||
"inactive": "禁用",
|
"inactive": "禁用",
|
||||||
"kind": "类型",
|
"kind": "类型",
|
||||||
"lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
|
"lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*\\.google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
|
||||||
"mailbox": "编辑邮箱",
|
"mailbox": "编辑邮箱",
|
||||||
"mailbox_quota_def": "邮箱默认配额",
|
"mailbox_quota_def": "邮箱默认配额",
|
||||||
"mailbox_relayhost_info": "只适用于邮箱和邮箱别名,不会覆盖域名的中继主机。",
|
"mailbox_relayhost_info": "只适用于邮箱和邮箱别名,不会覆盖域名的中继主机。",
|
||||||
|
@@ -213,7 +213,7 @@
|
|||||||
"loading": "請稍等...",
|
"loading": "請稍等...",
|
||||||
"login_time": "登入時間",
|
"login_time": "登入時間",
|
||||||
"logo_info": "你的起始頁面圖片會在頂部導覽列的限制下被縮放為 40px 高,以及最大 250px 高度。強烈推薦使用能較好縮放的圖片。",
|
"logo_info": "你的起始頁面圖片會在頂部導覽列的限制下被縮放為 40px 高,以及最大 250px 高度。強烈推薦使用能較好縮放的圖片。",
|
||||||
"lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
|
"lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*\\.google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
|
||||||
"main_name": "\"mailcow UI\" 名稱",
|
"main_name": "\"mailcow UI\" 名稱",
|
||||||
"merged_vars_hint": "灰色列來自 <code>vars.(local.)inc.php</code> 並且不能修改。",
|
"merged_vars_hint": "灰色列來自 <code>vars.(local.)inc.php</code> 並且不能修改。",
|
||||||
"message": "訊息",
|
"message": "訊息",
|
||||||
@@ -540,7 +540,7 @@
|
|||||||
"inactive": "停用",
|
"inactive": "停用",
|
||||||
"kind": "種類",
|
"kind": "種類",
|
||||||
"last_modified": "上次修改時間",
|
"last_modified": "上次修改時間",
|
||||||
"lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
|
"lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*\\.google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
|
||||||
"mailbox": "編輯信箱",
|
"mailbox": "編輯信箱",
|
||||||
"mailbox_quota_def": "預設信箱容量配額",
|
"mailbox_quota_def": "預設信箱容量配額",
|
||||||
"mailbox_relayhost_info": "只會套用於信箱和直接別名,不會覆寫域名中繼主機。",
|
"mailbox_relayhost_info": "只會套用於信箱和直接別名,不會覆寫域名中繼主機。",
|
||||||
|
@@ -60,7 +60,7 @@ elseif (isset($_GET['login'])) {
|
|||||||
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
||||||
));
|
));
|
||||||
// redirect to sogo (sogo will get the correct credentials via nginx auth_request
|
// redirect to sogo (sogo will get the correct credentials via nginx auth_request
|
||||||
header("Location: /SOGo/so/${login}");
|
header("Location: /SOGo/so/{$login}");
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,14 @@
|
|||||||
<label for="f2b_ban_time">{{ lang.admin.f2b_ban_time }}:</label>
|
<label for="f2b_ban_time">{{ lang.admin.f2b_ban_time }}:</label>
|
||||||
<input type="number" class="form-control" id="f2b_ban_time" name="ban_time" value="{{ f2b_data.ban_time }}" required>
|
<input type="number" class="form-control" id="f2b_ban_time" name="ban_time" value="{{ f2b_data.ban_time }}" required>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="f2b_max_ban_time">{{ lang.admin.f2b_max_ban_time }}:</label>
|
||||||
|
<input type="number" class="form-control" id="f2b_max_ban_time" name="max_ban_time" value="{{ f2b_data.max_ban_time }}" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<input class="form-check-input" type="checkbox" value="1" name="ban_time_increment" id="f2b_ban_time_increment" {% if f2b_data.ban_time_increment == 1 %}checked{% endif %}>
|
||||||
|
<label class="form-check-label" for="f2b_ban_time_increment">{{ lang.admin.f2b_ban_time_increment }}</label>
|
||||||
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="f2b_max_attempts">{{ lang.admin.f2b_max_attempts }}:</label>
|
<label for="f2b_max_attempts">{{ lang.admin.f2b_max_attempts }}:</label>
|
||||||
<input type="number" class="form-control" id="f2b_max_attempts" name="max_attempts" value="{{ f2b_data.max_attempts }}" required>
|
<input type="number" class="form-control" id="f2b_max_attempts" name="max_attempts" value="{{ f2b_data.max_attempts }}" required>
|
||||||
|
@@ -49,6 +49,12 @@
|
|||||||
<p><b>{{ hostname }}</b></p>
|
<p><b>{{ hostname }}</b></p>
|
||||||
</div></td>
|
</div></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ lang.debug.architecture }}</td>
|
||||||
|
<td class="text-break"><div>
|
||||||
|
<p id="host_architecture">-</p>
|
||||||
|
</div></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>IPs</td>
|
<td>IPs</td>
|
||||||
<td class="text-break">
|
<td class="text-break">
|
||||||
@@ -70,7 +76,7 @@
|
|||||||
<td>Version</td>
|
<td>Version</td>
|
||||||
<td class="text-break">
|
<td class="text-break">
|
||||||
<div class="fw-bolder">
|
<div class="fw-bolder">
|
||||||
<p ><a href="#" id="maiclow_version">{{ mailcow_info.version_tag }}</a></p>
|
<p ><a href="#" id="mailcow_version">{{ mailcow_info.version_tag }}</a></p>
|
||||||
<p id="mailcow_update"></p>
|
<p id="mailcow_update"></p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@@ -109,25 +109,25 @@
|
|||||||
<label class="control-label col-sm-2">{{ lang.user.quarantine_notification }}</label>
|
<label class="control-label col-sm-2">{{ lang.user.quarantine_notification }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="btn-group" data-acl="{{ acl.quarantine_notification }}">
|
<div class="btn-group" data-acl="{{ acl.quarantine_notification }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'never' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'never' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
data-api-url='edit/quarantine_notification'
|
data-api-url='edit/quarantine_notification'
|
||||||
data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button>
|
data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'hourly' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'hourly' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
data-api-url='edit/quarantine_notification'
|
data-api-url='edit/quarantine_notification'
|
||||||
data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button>
|
data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'daily' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'daily' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
data-api-url='edit/quarantine_notification'
|
data-api-url='edit/quarantine_notification'
|
||||||
data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button>
|
data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'weekly' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'weekly' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
@@ -141,19 +141,19 @@
|
|||||||
<label class="control-label col-sm-2">{{ lang.user.quarantine_category }}</label>
|
<label class="control-label col-sm-2">{{ lang.user.quarantine_category }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="btn-group" data-acl="{{ acl.quarantine_category }}">
|
<div class="btn-group" data-acl="{{ acl.quarantine_category }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'reject' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'reject' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_category"
|
data-id="quarantine_category"
|
||||||
data-api-url='edit/quarantine_category'
|
data-api-url='edit/quarantine_category'
|
||||||
data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button>
|
data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'add_header' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'add_header' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_category"
|
data-id="quarantine_category"
|
||||||
data-api-url='edit/quarantine_category'
|
data-api-url='edit/quarantine_category'
|
||||||
data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button>
|
data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'all' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'all' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="quarantine_category"
|
data-id="quarantine_category"
|
||||||
@@ -167,13 +167,13 @@
|
|||||||
<label class="control-label col-sm-2" for="sender_acl">{{ lang.user.tls_policy }}</label>
|
<label class="control-label col-sm-2" for="sender_acl">{{ lang.user.tls_policy }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="btn-group" data-acl="{{ acl.tls_policy }}">
|
<div class="btn-group" data-acl="{{ acl.tls_policy }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_in == '1' %} active"{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_in == '1' %} btn-dark"{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="tls_policy"
|
data-id="tls_policy"
|
||||||
data-api-url='edit/tls_policy'
|
data-api-url='edit/tls_policy'
|
||||||
data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button>
|
data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_out == '1' %} active"{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_out == '1' %} btn-dark"{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailbox }}"
|
data-item="{{ mailbox }}"
|
||||||
data-id="tls_policy"
|
data-id="tls_policy"
|
||||||
|
@@ -54,6 +54,7 @@
|
|||||||
<li class="dropdown-header">SMTP</li>
|
<li class="dropdown-header">SMTP</li>
|
||||||
<li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"smtp_access":1}' href="#">{{ lang.mailbox.activate }}</a></li>
|
<li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"smtp_access":1}' href="#">{{ lang.mailbox.activate }}</a></li>
|
||||||
<li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"smtp_access":0}' href="#">{{ lang.mailbox.deactivate }}</a></li>
|
<li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"smtp_access":0}' href="#">{{ lang.mailbox.deactivate }}</a></li>
|
||||||
|
<li><hr class="dropdown-divider"></li>
|
||||||
<li class="dropdown-header">Sieve</li>
|
<li class="dropdown-header">Sieve</li>
|
||||||
<li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"sieve_access":1}' href="#">{{ lang.mailbox.activate }}</a></li>
|
<li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"sieve_access":1}' href="#">{{ lang.mailbox.activate }}</a></li>
|
||||||
<li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"sieve_access":0}' href="#">{{ lang.mailbox.deactivate }}</a></li>
|
<li><a class="dropdown-item" data-action="edit_selected" data-id="mailbox" data-api-url='edit/mailbox' data-api-attr='{"sieve_access":0}' href="#">{{ lang.mailbox.deactivate }}</a></li>
|
||||||
|
@@ -12,11 +12,21 @@
|
|||||||
<li><button class="dropdown-item" role="tab" aria-selected="false" aria-controls="tab-config-f2b" data-bs-toggle="tab" data-bs-target="#tab-user-settings">{{ lang.user.mailbox_settings }}</button></li>
|
<li><button class="dropdown-item" role="tab" aria-selected="false" aria-controls="tab-config-f2b" data-bs-toggle="tab" data-bs-target="#tab-user-settings">{{ lang.user.mailbox_settings }}</button></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
{% if acl.spam_alias == 1 %}
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="SpamAliases" role="tab" data-bs-toggle="tab" data-bs-target="#SpamAliases">{{ lang.user.spam_aliases }}</button></li>
|
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="SpamAliases" role="tab" data-bs-toggle="tab" data-bs-target="#SpamAliases">{{ lang.user.spam_aliases }}</button></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if acl.spam_score == 1 %}
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Spamfilter" role="tab" data-bs-toggle="tab" data-bs-target="#Spamfilter">{{ lang.user.spamfilter }}</button></li>
|
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Spamfilter" role="tab" data-bs-toggle="tab" data-bs-target="#Spamfilter">{{ lang.user.spamfilter }}</button></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if acl.syncjobs == 1 %}
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Syncjobs" role="tab" data-bs-toggle="tab" data-bs-target="#Syncjobs">{{ lang.user.sync_jobs }}</button></li>
|
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Syncjobs" role="tab" data-bs-toggle="tab" data-bs-target="#Syncjobs">{{ lang.user.sync_jobs }}</button></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if acl.app_passwds == 1 %}
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="AppPasswds" role="tab" data-bs-toggle="tab" data-bs-target="#AppPasswds">{{ lang.user.app_passwds }}</button></li>
|
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="AppPasswds" role="tab" data-bs-toggle="tab" data-bs-target="#AppPasswds">{{ lang.user.app_passwds }}</button></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if acl.pushover == 1 %}
|
||||||
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Pushover" role="tab" data-bs-toggle="tab" data-bs-target="#Pushover">Pushover API</button></li>
|
<li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Pushover" role="tab" data-bs-toggle="tab" data-bs-target="#Pushover">Pushover API</button></li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -25,11 +35,11 @@
|
|||||||
{% include 'user/tab-user-auth.twig' %}
|
{% include 'user/tab-user-auth.twig' %}
|
||||||
{% include 'user/tab-user-details.twig' %}
|
{% include 'user/tab-user-details.twig' %}
|
||||||
{% include 'user/tab-user-settings.twig' %}
|
{% include 'user/tab-user-settings.twig' %}
|
||||||
{% include 'user/SpamAliases.twig' %}
|
{% if acl.spam_alias == 1 %}{% include 'user/SpamAliases.twig' %}{% endif %}
|
||||||
{% include 'user/Spamfilter.twig' %}
|
{% if acl.spam_score == 1 %}{% include 'user/Spamfilter.twig' %}{% endif %}
|
||||||
{% include 'user/Syncjobs.twig' %}
|
{% if acl.syncjobs == 1 %}{% include 'user/Syncjobs.twig' %}{% endif %}
|
||||||
{% include 'user/AppPasswds.twig' %}
|
{% if acl.app_passwds == 1 %}{% include 'user/AppPasswds.twig' %}{% endif %}
|
||||||
{% include 'user/Pushover.twig' %}
|
{% if acl.pushover == 1 %}{% include 'user/Pushover.twig' %}{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -12,19 +12,19 @@
|
|||||||
<div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.tag_handling }}:</div>
|
<div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.tag_handling }}:</div>
|
||||||
<div class="col-sm-9 col-12">
|
<div class="col-sm-9 col-12">
|
||||||
<div class="btn-group" data-acl="{{ acl.delimiter_action }}">
|
<div class="btn-group" data-acl="{{ acl.delimiter_action }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if get_tagging_options == 'subfolder' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if get_tagging_options == 'subfolder' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="delimiter_action"
|
data-id="delimiter_action"
|
||||||
data-api-url='edit/delimiter_action'
|
data-api-url='edit/delimiter_action'
|
||||||
data-api-attr='{"tagged_mail_handler":"subfolder"}'>{{ lang.user.tag_in_subfolder }}</button>
|
data-api-attr='{"tagged_mail_handler":"subfolder"}'>{{ lang.user.tag_in_subfolder }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if get_tagging_options == 'subject' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if get_tagging_options == 'subject' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="delimiter_action"
|
data-id="delimiter_action"
|
||||||
data-api-url='edit/delimiter_action'
|
data-api-url='edit/delimiter_action'
|
||||||
data-api-attr='{"tagged_mail_handler":"subject"}'>{{ lang.user.tag_in_subject }}</button>
|
data-api-attr='{"tagged_mail_handler":"subject"}'>{{ lang.user.tag_in_subject }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if get_tagging_options == 'none' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if get_tagging_options == 'none' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="delimiter_action"
|
data-id="delimiter_action"
|
||||||
@@ -40,13 +40,13 @@
|
|||||||
<div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.tls_policy }}:</div>
|
<div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.tls_policy }}:</div>
|
||||||
<div class="col-sm-9 col-12">
|
<div class="col-sm-9 col-12">
|
||||||
<div class="btn-group" data-acl="{{ acl.tls_policy }}">
|
<div class="btn-group" data-acl="{{ acl.tls_policy }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_in == '1' %} active"{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_in == '1' %} btn-dark"{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="tls_policy"
|
data-id="tls_policy"
|
||||||
data-api-url='edit/tls_policy'
|
data-api-url='edit/tls_policy'
|
||||||
data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button>
|
data-api-attr='{"tls_enforce_in": {% if get_tls_policy.tls_enforce_in == '1' %}0{% else %}1{% endif %} }'>{{ lang.user.tls_enforce_in }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary{% if get_tls_policy.tls_enforce_out == '1' %} active"{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-half d-block d-sm-inline btn-light{% if get_tls_policy.tls_enforce_out == '1' %} btn-dark"{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="tls_policy"
|
data-id="tls_policy"
|
||||||
@@ -61,25 +61,25 @@
|
|||||||
<div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.quarantine_notification }}:</div>
|
<div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.quarantine_notification }}:</div>
|
||||||
<div class="col-sm-9 col-12">
|
<div class="col-sm-9 col-12">
|
||||||
<div class="btn-group" data-acl="{{ acl.quarantine_notification }}">
|
<div class="btn-group" data-acl="{{ acl.quarantine_notification }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'never' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'never' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
data-api-url='edit/quarantine_notification'
|
data-api-url='edit/quarantine_notification'
|
||||||
data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button>
|
data-api-attr='{"quarantine_notification":"never"}'>{{ lang.user.never }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'hourly' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'hourly' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
data-api-url='edit/quarantine_notification'
|
data-api-url='edit/quarantine_notification'
|
||||||
data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button>
|
data-api-attr='{"quarantine_notification":"hourly"}'>{{ lang.user.hourly }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'daily' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'daily' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
data-api-url='edit/quarantine_notification'
|
data-api-url='edit/quarantine_notification'
|
||||||
data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button>
|
data-api-attr='{"quarantine_notification":"daily"}'>{{ lang.user.daily }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-secondary{% if quarantine_notification == 'weekly' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-quart d-block d-sm-inline btn-light{% if quarantine_notification == 'weekly' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="quarantine_notification"
|
data-id="quarantine_notification"
|
||||||
@@ -93,19 +93,19 @@
|
|||||||
<div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.quarantine_category }}:</div>
|
<div class="col-sm-3 col-12 text-sm-end text-start text-xs-bold mb-4">{{ lang.user.quarantine_category }}:</div>
|
||||||
<div class="col-sm-9 col-12">
|
<div class="col-sm-9 col-12">
|
||||||
<div class="btn-group" data-acl="{{ acl.quarantine_category }}">
|
<div class="btn-group" data-acl="{{ acl.quarantine_category }}">
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'reject' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'reject' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="quarantine_category"
|
data-id="quarantine_category"
|
||||||
data-api-url='edit/quarantine_category'
|
data-api-url='edit/quarantine_category'
|
||||||
data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button>
|
data-api-attr='{"quarantine_category":"reject"}'>{{ lang.user.q_reject }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'add_header' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'add_header' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="quarantine_category"
|
data-id="quarantine_category"
|
||||||
data-api-url='edit/quarantine_category'
|
data-api-url='edit/quarantine_category'
|
||||||
data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button>
|
data-api-attr='{"quarantine_category":"add_header"}'>{{ lang.user.q_add_header }}</button>
|
||||||
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-secondary{% if quarantine_category == 'all' %} active{% endif %}"
|
<button type="button" class="btn btn-sm btn-xs-third d-block d-sm-inline btn-light{% if quarantine_category == 'all' %} btn-dark{% endif %}"
|
||||||
data-action="edit_selected"
|
data-action="edit_selected"
|
||||||
data-item="{{ mailcow_cc_username }}"
|
data-item="{{ mailcow_cc_username }}"
|
||||||
data-id="quarantine_category"
|
data-id="quarantine_category"
|
||||||
|
@@ -76,7 +76,7 @@ services:
|
|||||||
- clamd
|
- clamd
|
||||||
|
|
||||||
rspamd-mailcow:
|
rspamd-mailcow:
|
||||||
image: mailcow/rspamd:1.93
|
image: mailcow/rspamd:1.92
|
||||||
stop_grace_period: 30s
|
stop_grace_period: 30s
|
||||||
depends_on:
|
depends_on:
|
||||||
- dovecot-mailcow
|
- dovecot-mailcow
|
||||||
@@ -106,7 +106,7 @@ services:
|
|||||||
- rspamd
|
- rspamd
|
||||||
|
|
||||||
php-fpm-mailcow:
|
php-fpm-mailcow:
|
||||||
image: mailcow/phpfpm:1.83
|
image: mailcow/phpfpm:1.84
|
||||||
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis-mailcow
|
- redis-mailcow
|
||||||
@@ -169,7 +169,7 @@ services:
|
|||||||
- phpfpm
|
- phpfpm
|
||||||
|
|
||||||
sogo-mailcow:
|
sogo-mailcow:
|
||||||
image: mailcow/sogo:1.115
|
image: mailcow/sogo:1.117
|
||||||
environment:
|
environment:
|
||||||
- DBNAME=${DBNAME}
|
- DBNAME=${DBNAME}
|
||||||
- DBUSER=${DBUSER}
|
- DBUSER=${DBUSER}
|
||||||
@@ -425,7 +425,7 @@ services:
|
|||||||
- acme
|
- acme
|
||||||
|
|
||||||
netfilter-mailcow:
|
netfilter-mailcow:
|
||||||
image: mailcow/netfilter:1.51
|
image: mailcow/netfilter:1.52
|
||||||
stop_grace_period: 30s
|
stop_grace_period: 30s
|
||||||
depends_on:
|
depends_on:
|
||||||
- dovecot-mailcow
|
- dovecot-mailcow
|
||||||
@@ -510,7 +510,7 @@ services:
|
|||||||
- watchdog
|
- watchdog
|
||||||
|
|
||||||
dockerapi-mailcow:
|
dockerapi-mailcow:
|
||||||
image: mailcow/dockerapi:2.01
|
image: mailcow/dockerapi:2.04
|
||||||
security_opt:
|
security_opt:
|
||||||
- label=disable
|
- label=disable
|
||||||
restart: always
|
restart: always
|
||||||
|
@@ -261,7 +261,7 @@ COMPOSE_PROJECT_NAME=mailcowdockerized
|
|||||||
# Switch here between native (compose plugin) and standalone
|
# Switch here between native (compose plugin) and standalone
|
||||||
# For more informations take a look at the mailcow docs regarding the configuration options.
|
# For more informations take a look at the mailcow docs regarding the configuration options.
|
||||||
# Normally this should be untouched but if you decided to use either of those you can switch it manually here.
|
# Normally this should be untouched but if you decided to use either of those you can switch it manually here.
|
||||||
# Please be aware that at least one of those variants should be installed on your maschine or mailcow will fail.
|
# Please be aware that at least one of those variants should be installed on your machine or mailcow will fail.
|
||||||
|
|
||||||
DOCKER_COMPOSE_VERSION=${COMPOSE_VERSION}
|
DOCKER_COMPOSE_VERSION=${COMPOSE_VERSION}
|
||||||
|
|
||||||
|
@@ -26,6 +26,6 @@ services:
|
|||||||
- /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysqld.sock
|
- /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysqld.sock
|
||||||
|
|
||||||
mysql-mailcow:
|
mysql-mailcow:
|
||||||
image: alpine:3.17
|
image: alpine:3.18
|
||||||
command: /bin/true
|
command: /bin/true
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
|
# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
|
||||||
NEXTCLOUD_VERSION=26.0.0
|
NEXTCLOUD_VERSION=26.0.1
|
||||||
|
|
||||||
echo -ne "Checking prerequisites..."
|
echo -ne "Checking prerequisites..."
|
||||||
sleep 1
|
sleep 1
|
||||||
@@ -97,8 +97,12 @@ elif [[ ${NC_UPDATE} == "y" ]]; then
|
|||||||
echo -e "\033[31mError: Nextcloud occ not found. Is Nextcloud installed?\033[0m"
|
echo -e "\033[31mError: Nextcloud occ not found. Is Nextcloud installed?\033[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! grep -q 'installed: true' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then
|
if grep -Pq 'This version of Nextcloud is not compatible with (?:PHP)?(?>=?)(?:PHP)?(?>.+)' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then
|
||||||
echo "Nextcloud seems not to be installed."
|
echo -e "\033[31mError: This version of Nextcloud is not compatible with the current PHP version of php-fpm-mailcow, we'll fix it\033[0m"
|
||||||
|
wget -q https://raw.githubusercontent.com/nextcloud/server/v26.0.0/lib/versioncheck.php -O ./data/web/nextcloud/lib/versioncheck.php
|
||||||
|
echo -e "\e[33mPlease restart the update again.\e[0m"
|
||||||
|
elif ! grep -q 'installed: true' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then
|
||||||
|
echo -e "\033[31mError: Nextcloud seems not to be installed.\033[0m"
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "php /web/nextcloud/updater/updater.phar"
|
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "php /web/nextcloud/updater/updater.phar"
|
||||||
|
@@ -6,7 +6,7 @@ SPFTOOLS_DIR=${WORKING_DIR}/spf-tools
|
|||||||
POSTWHITE_DIR=${WORKING_DIR}/postwhite
|
POSTWHITE_DIR=${WORKING_DIR}/postwhite
|
||||||
POSTWHITE_CONF=${POSTWHITE_DIR}/postwhite.conf
|
POSTWHITE_CONF=${POSTWHITE_DIR}/postwhite.conf
|
||||||
|
|
||||||
COSTOM_HOSTS="web.de gmx.net mail.de freenet.de arcor.de unity-mail.de"
|
CUSTOM_HOSTS='"web.de gmx.net mail.de freenet.de arcor.de unity-mail.de"'
|
||||||
STATIC_HOSTS=(
|
STATIC_HOSTS=(
|
||||||
"194.25.134.0/24 permit # t-online.de"
|
"194.25.134.0/24 permit # t-online.de"
|
||||||
)
|
)
|
||||||
@@ -19,13 +19,20 @@ function set_config() {
|
|||||||
sudo sed -i "s@^\($1\s*=\s*\).*\$@\1$2@" ${POSTWHITE_CONF}
|
sudo sed -i "s@^\($1\s*=\s*\).*\$@\1$2@" ${POSTWHITE_CONF}
|
||||||
}
|
}
|
||||||
|
|
||||||
set_config custom_hosts ${COSTOM_HOSTS}
|
set_config custom_hosts "${CUSTOM_HOSTS}"
|
||||||
set_config reload_postfix no
|
set_config reload_postfix no
|
||||||
set_config postfixpath /.
|
set_config postfixpath /.
|
||||||
set_config spftoolspath ${WORKING_DIR}/spf-tools
|
set_config spftoolspath ${WORKING_DIR}/spf-tools
|
||||||
set_config whitelist .${SCRIPT_DIR}/../data/conf/postfix/postscreen_access.cidr
|
set_config whitelist .${SCRIPT_DIR}/../data/conf/postfix/postscreen_access.cidr
|
||||||
set_config yahoo_static_hosts ${POSTWHITE_DIR}/yahoo_static_hosts.txt
|
set_config yahoo_static_hosts ${POSTWHITE_DIR}/yahoo_static_hosts.txt
|
||||||
|
|
||||||
|
#Fix URL for Yahoo!: https://github.com/stevejenkins/postwhite/issues/59
|
||||||
|
sudo sed -i \
|
||||||
|
-e 's#yahoo_url="https://help.yahoo.com/kb/SLN23997.html"#yahoo_url="https://senders.yahooinc.com/outbound-mail-servers/"#' \
|
||||||
|
-e 's#echo "ipv6:$line";#echo "ipv6:$line" | grep -v "ipv6:::";#' \
|
||||||
|
-e 's#`command -v wget`#`command -v skip-wget`#' \
|
||||||
|
${POSTWHITE_DIR}/scrape_yahoo
|
||||||
|
|
||||||
cd ${POSTWHITE_DIR}
|
cd ${POSTWHITE_DIR}
|
||||||
./postwhite ${POSTWHITE_CONF}
|
./postwhite ${POSTWHITE_CONF}
|
||||||
|
|
||||||
|
10
update.sh
10
update.sh
@@ -188,7 +188,7 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
|
|||||||
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
|
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
|
||||||
else
|
else
|
||||||
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
||||||
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
elif docker-compose > /dev/null 2>&1; then
|
elif docker-compose > /dev/null 2>&1; then
|
||||||
@@ -203,14 +203,14 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
|
|||||||
echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
|
echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
|
||||||
else
|
else
|
||||||
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
||||||
echo -e "\e[31mPlease update/install regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease update/install regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
echo -e "\e[31mCannot find Docker Compose.\e[0m"
|
echo -e "\e[31mCannot find Docker Compose.\e[0m"
|
||||||
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then
|
|||||||
if ! $COMPOSE_COMMAND > /dev/null 2>&1 || ! $COMPOSE_COMMAND --version | grep "^2." > /dev/null 2>&1; then
|
if ! $COMPOSE_COMMAND > /dev/null 2>&1 || ! $COMPOSE_COMMAND --version | grep "^2." > /dev/null 2>&1; then
|
||||||
# IF it cannot find Standalone in > 2.X, then script stops
|
# IF it cannot find Standalone in > 2.X, then script stops
|
||||||
echo -e "\e[31mCannot find Docker Compose or the Version is lower then 2.X.X.\e[0m"
|
echo -e "\e[31mCannot find Docker Compose or the Version is lower then 2.X.X.\e[0m"
|
||||||
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# If it finds the standalone Plugin it will use this instead and change the mailcow.conf Variable accordingly
|
# If it finds the standalone Plugin it will use this instead and change the mailcow.conf Variable accordingly
|
||||||
@@ -243,7 +243,7 @@ elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
|
|||||||
if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
|
if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
|
||||||
# IF it cannot find Native in > 2.X, then script stops
|
# IF it cannot find Native in > 2.X, then script stops
|
||||||
echo -e "\e[31mCannot find Docker Compose.\e[0m"
|
echo -e "\e[31mCannot find Docker Compose.\e[0m"
|
||||||
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# If it finds the native Plugin it will use this instead and change the mailcow.conf Variable accordingly
|
# If it finds the native Plugin it will use this instead and change the mailcow.conf Variable accordingly
|
||||||
|
Reference in New Issue
Block a user