Compare commits

..

105 Commits

Author SHA1 Message Date
Patrick Schult
8f28666916 Merge pull request #5195 from mailcow/staging
2023-04b
2023-04-20 16:49:17 +02:00
Patrick Schult
3eaa5a626c Merge pull request #5187 from mailcow/fix-5185
Nextcloud helperscript - redo PHP check
2023-04-20 14:20:03 +02:00
Patrick Schult
8c79056a94 Merge pull request #5194 from mailcow/renovate/nextcloud-server-26.x
Update dependency nextcloud/server to v26.0.1
2023-04-20 14:19:19 +02:00
Patrick Schult
ed076dc23e Merge pull request #5186 from goodygh/datatables_sorting
[Web] Datatables sorting
2023-04-20 13:50:57 +02:00
FreddleSpl0it
be2286c11c [Dockerapi] fix maildir cleanup for domains 2023-04-20 13:41:11 +02:00
renovate[bot]
0e24c3d300 Update dependency nextcloud/server to v26.0.1
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-04-20 11:36:01 +00:00
FreddleSpl0it
e1d8df6580 [Web] check mailbox before replacing sogo_static_view 2023-04-20 13:20:51 +02:00
Patrick Schult
04a08a7d69 Merge pull request #5193 from mailcow/feat/update-sogo
[SOGo] update sogo 5.8.2.20230419
2023-04-20 12:32:42 +02:00
FreddleSpl0it
3c0c8aa01f [SOGo] update sogo 5.8.2.20230419 2023-04-20 12:07:21 +02:00
Patrick Schult
026b278357 Merge pull request #5183 from mailcow/fix/add-mbox-performance
[Web] optimizing mailbox add/edit/delete performance
2023-04-20 11:34:41 +02:00
FreddleSpl0it
4121509ceb [Web] optimizing update_sogo_static_view function 2023-04-20 11:28:59 +02:00
Patrick Schult
00ac61f0a4 Merge pull request #5184 from bdwebnet/fix/ui-allowed-protocols
Added dropdown divider to "allowed protocols" selection on mailbox page
2023-04-19 17:31:05 +02:00
Patrick Schult
4bb0dbb2f7 Merge pull request #5191 from shiz0/patch-1
Fix Typo
2023-04-19 17:26:54 +02:00
Patrick Schult
13b6df74af Merge pull request #5174 from bdwebnet/staging
Fix error  "Deprecated: Using ${var} in strings is deprecated, use {$…
2023-04-19 17:23:26 +02:00
FreddleSpl0it
5c025bf865 [Rspamd] rollback to 3.4 2023-04-19 17:03:04 +02:00
Hannes Happle
20fc9eaf84 Fix Typo 2023-04-16 14:32:44 +02:00
Peter
22a0479fab Redo the PHP check grep 2023-04-13 21:11:40 +02:00
goodygh
3510d5617d Fix sorting for active relayhost 2023-04-13 19:18:04 +02:00
goodygh
236d627fbd Fix sorting for active transport map 2023-04-13 19:14:20 +02:00
goodygh
99739eada0 Fix sorting for active fowrardinghoststable 2023-04-13 19:01:03 +02:00
goodygh
7bfef57894 Fix sorting for active and tla on admins 2023-04-13 18:54:59 +02:00
goodygh
d9dfe15253 Fix sorting for active and tla on domain-admins 2023-04-13 18:54:08 +02:00
goodygh
3fe8aaa719 Fix sorting for active tls-policy-map 2023-04-13 18:14:18 +02:00
goodygh
78a8fac6af Fix sorting for active bcc-map and recipient-map 2023-04-13 18:10:21 +02:00
bd
6986e7758f Added dropdown divider to "allowed protocols" selection on mailbox page 2023-04-13 17:33:28 +02:00
BD
b4a9df76b8 Merge branch 'mailcow:staging' into staging 2023-04-13 17:22:13 +02:00
FreddleSpl0it
d9d958356a [Web] optimizing update_sogo_static_view function 2023-04-13 14:35:55 +02:00
goodygh
96f954a4e2 Fix sorting for active syncjobs 2023-04-12 00:36:46 +02:00
goodygh
44585e1c15 Fix sorting datatable in domain aliases 2023-04-12 00:23:53 +02:00
goodygh
c737ff4180 Fix sorting datatable in aliases 2023-04-12 00:21:27 +02:00
goodygh
025279009d Fix sorting for active resources 2023-04-12 00:17:41 +02:00
goodygh
a9dc13d567 Fix sorting datatable in mailbox templates 2023-04-12 00:15:16 +02:00
goodygh
c3ed01c9b5 Fix sorting for active mailboxes 2023-04-11 23:49:50 +02:00
goodygh
bd0b4a521e Fix sorting datatable in domain templates 2023-04-11 23:42:43 +02:00
goodygh
800a0ace71 Fix sorting for active domain in domains table 2023-04-11 23:19:56 +02:00
goodygh
db97869472 Datatable hide sorting value 2023-04-11 23:18:13 +02:00
milkmaker
f681fcf154 [Web] Updated lang.cs-cz.json (#5177)
Co-authored-by: utaxiu <kontakt@malyjakub.cz>
2023-04-11 17:38:39 +02:00
Patrick Schult
db1b5956fc Merge pull request #5133 from FELDSAM-INC/feldsam/bs5-related-fixes
BS5 related fixes
2023-04-11 06:35:41 +02:00
BD
bdb07061ed Fix error "Deprecated: Using ${var} in strings is deprecated, use {$var} instead in /web/sogo-auth.php on line 63" 2023-04-08 17:29:34 +02:00
Niklas Meyer
428b917579 Merge pull request #5166 from mailcow/staging
Hotfix php8.2 nextcloud < 26
2023-04-03 20:15:46 +02:00
Niklas Meyer
469f959e96 Merge pull request #5164 from mailcow/fix-5163
Add a check for PHP>=8.2 errormsg
2023-04-03 20:10:05 +02:00
Peter
b68e189d97 Add a check for PHP>=8.2 errormsg 2023-04-03 19:03:13 +02:00
Niklas Meyer
028ef22878 Merge pull request #5162 from mailcow/staging
Update 2023-04
2023-04-03 14:55:55 +02:00
Kristian Feldsam
80dacc015a [web] fixed mailbox/user settings buttons styling
Signed-off-by: Kristian Feldsam <feldsam@gmail.com>

[web] fixed mailbox/user settings buttons styling

Signed-off-by: Kristian Feldsam <feldsam@gmail.com>
2023-03-31 13:19:20 +02:00
Patrick Schult
0194c39bd5 Merge pull request #5158 from mailcow/feat/sogo-5.8.2
[SOGo] Update to 5.8.2
2023-03-31 08:16:57 +02:00
FreddleSpl0it
f53ca24bb0 [SOGo] Update to 5.8.2 2023-03-30 16:00:21 +02:00
Patrick Schult
ae46a877d3 Merge pull request #5157 from mailcow/feat/netfilter-1.52
[Netfilter] Update to 1.52
2023-03-30 09:05:52 +02:00
FreddleSpl0it
400939faf6 [Netfilter] Update to 1.52 2023-03-30 08:44:38 +02:00
Patrick Schult
fd0205aafd Merge pull request #5127 from th-joerger/feature/bantime-increment
[Netfilter] Implemented exponentially incrementing bantime
2023-03-30 07:53:33 +02:00
Patrick Schult
e367a8ce24 Merge pull request #5153 from mailcow/fix/del-vmail-index
[Dockerapi] delete vmail_index on maildir cleanup
2023-03-30 07:52:00 +02:00
Thorbjörn Jörger
096e2a41e9 Push verified options to redis after each check 2023-03-29 17:09:25 +02:00
Thorbjörn Jörger
e010f08143 verify options after loading them, set defaults if options are missing or invalid 2023-03-29 15:24:14 +02:00
Patrick Schult
3d2483ca37 Merge pull request #5093 from brunoleon/fix_snat
Fix SNAT never being added because of exception
2023-03-29 08:13:11 +02:00
Niklas Meyer
535dd23509 Merge pull request #5139 from mailcow/renovate/mailcow-rspamd-1.x
Update mailcow/rspamd Docker tag to v1.93
2023-03-28 11:44:59 +02:00
DerLinkman
4336a99c6a [Nextcloud] Changed default X-Robots Tag behavior 2023-03-28 11:40:00 +02:00
DerLinkman
4cd5f93cdf Fixed broken pipe errors in nextcloud.sh 2023-03-28 11:22:49 +02:00
DerLinkman
67955779b0 Fix broken pipe error in reset-admin.sh 2023-03-28 11:17:59 +02:00
FreddleSpl0it
26c34b484a increase dockerapi image 2023-03-28 11:01:14 +02:00
FreddleSpl0it
4021613059 delete vmail_index when mbox is deleted 2023-03-28 10:59:08 +02:00
Niklas Meyer
e891bf8411 Merge pull request #5138 from th-joerger/feature/pubsub-exception
[netfilter] add pubsub exception
2023-03-27 10:40:40 +02:00
Niklas Meyer
f7798d1aac Merge pull request #5099 from mailcow/feat/phpfpm-8.2
Update to PHP 8.2
2023-03-27 10:13:42 +02:00
Niklas Meyer
d11f00261b Merge pull request #5142 from mailcow/renovate/nextcloud-server-26.x
Update dependency nextcloud/server to v26
2023-03-27 10:12:55 +02:00
renovate[bot]
22cd12f37b Update dependency nextcloud/server to v26
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-03-25 18:48:22 +00:00
Peter
db2fb12837 Install sysvsem for Nextcloud 26 2023-03-24 16:08:19 +01:00
Peter
e808e595eb Update dependency composer/composer to v2.5.5 2023-03-24 16:05:35 +01:00
Niklas Meyer
ce6742c676 Merge pull request #5147 from mailcow/renovate/nextcloud-server-25.x
Update dependency nextcloud/server to v25.0.5
2023-03-23 19:38:23 +01:00
renovate[bot]
cf3dc584d0 Update dependency nextcloud/server to v25.0.5
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-03-23 14:18:29 +00:00
renovate[bot]
62f3603588 Update actions/stale action to v8 (#5143)
Signed-off-by: milkmaker <milkmaker@mailcow.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-22 15:00:55 +01:00
renovate[bot]
9fd4aa93e9 Update mailcow/rspamd Docker tag to v1.93
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-03-21 10:32:21 +00:00
Thorbjörn Jörger
5bc3d93545 log exception of redis pubsub subscription 2023-03-21 11:14:52 +01:00
Thorbjörn Jörger
c28a6b89f0 Added ban_time_increment and max_ban_time to UI 2023-03-21 11:06:13 +01:00
Thorbjörn Jörger
1233613bea implemented handling of max_bantime and ban_time_increment flag 2023-03-21 11:06:13 +01:00
Thorbjörn Jörger
0206e0886c implemented exponentially incrementing bantime, removed active_window code that did nothing, cleanly initialized dictionary 2023-03-21 11:06:13 +01:00
DerLinkman
f6d135fbad [Update.sh] Fix docker compose detection + added failover 2023-03-20 12:05:11 +01:00
Niklas Meyer
f7da314dcf Merge pull request #5134 from mailcow/fix/generate-config-dev
[Generate.sh] Fixed broken pipe error message
2023-03-20 11:08:11 +01:00
DerLinkman
e6ce5e88f7 [Generate.sh] Fixed broken pipe error message 2023-03-20 10:57:40 +01:00
Kristian Feldsam
e5e6418be8 [web] fixed tooltips in ajax loaded alias table
Signed-off-by: Kristian Feldsam <feldsam@gmail.com>
2023-03-20 01:38:34 +01:00
Kristian Feldsam
6507b53bbb [web] fix mailbox badge height
Signed-off-by: Kristian Feldsam <feldsam@gmail.com>
2023-03-20 01:38:31 +01:00
milkmaker
0f59d4952b Translations update from Weblate (#5131)
* [Web] Updated lang.da-dk.json

Co-authored-by: Victor Pahuus Petersen <dibbohh@gmail.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.fr-fr.json

Co-authored-by: UpSilot <alexandre+weblate@kilobit.fr>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

---------

Co-authored-by: Victor Pahuus Petersen <dibbohh@gmail.com>
Co-authored-by: UpSilot <alexandre+weblate@kilobit.fr>
2023-03-17 19:13:49 +01:00
Niklas Meyer
7225bd2f55 Merge pull request #5107 from kaechele:staging
Fix SELinux labelling of init_db.inc.php for SOGo
2023-03-09 14:37:21 +01:00
Niklas Meyer
deb2b80352 Merge pull request #5108 from mailcow:dragoangel-patch-1
[Rspamd] Fix cases of forwarding via freemail
2023-03-09 14:33:48 +01:00
Niklas Meyer
ad9dee92be Merge pull request #5119 from bdwebnet:staging
Fixes Issue #5118 (Bug with load more logs buttons)
2023-03-09 14:30:55 +01:00
BD
f36bc16ca7 Fix Bug with button to load more logs 2023-03-08 10:35:23 +01:00
Niklas Meyer
bda5f0ed4a Merge pull request #5109 from mailcow/dragoangel-patch-2
[SOGo] Disable password change option
2023-03-07 09:07:45 +01:00
milkmaker
cbe1c97a82 Translations update from Weblate (#5114)
* [Web] Updated lang.da-dk.json

[Web] Updated lang.da-dk.json

[Web] Updated lang.da-dk.json

Co-authored-by: Tacaly <frederick@tacaly.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.fr-fr.json

Co-authored-by: Matthieu Leboeuf <contact@matthieul.dev>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

---------

Co-authored-by: Tacaly <frederick@tacaly.com>
Co-authored-by: Matthieu Leboeuf <contact@matthieul.dev>
2023-03-07 05:39:22 +01:00
Dmitriy Alekseev
81fcbdd104 [SOGo] Disable password change option
It doesn't work with ProxyAuth and in general not honor password policy set via mailcow UI. SOGo also do not provide own settings to provide any password policy. Due to this two issues I think that it's better have it disabled by default. People who need it can turn it back easily. We can update https://docs.mailcow.email/manual-guides/SOGo/u_e-sogo/#disable-password-changing to `enable-password-changin` and explanations of reasons why it is disabled.
2023-03-04 18:06:26 +02:00
Dmitriy Alekseev
1a9294b58f [Rspamd] Fix cases of forwarding via freemail
Excluding FREEMAIL_ENVFROM from the FREEMAIL_POLICY_FAILURE expression will allow forwarding mail via freemail services when the initial sender did not have a DKIM signature.
2023-03-04 17:57:52 +02:00
Felix Kaechele
310c01aac2 Fix SELinux labelling of init_db.inc.php for SOGo
init_db.inc.php is currently labelled as exclusive for SOGo while in
truth it is shared among containers.
This breaks the admin interface but also any of the DAV features of
SOGo.

Signed-off-by: Felix Kaechele <felix@kaechele.ca>
2023-03-03 22:57:10 -05:00
Niklas Meyer
229303c1f8 Merge pull request #5106 from mailcow/staging
2023-03
2023-03-03 17:34:24 +01:00
Niklas Meyer
fc075bc6b7 Merge pull request #5104 from svengo/patch-4
[Helper] Update expiry-dates.sh
2023-03-03 12:44:00 +01:00
DerLinkman
d04f0257c2 Fixed permission for expiry-dates.sh 2023-03-03 12:41:24 +01:00
Sven Gottwald
d11d356803 [Helper] Update expiry-dates.sh
- Use port numbers from `mailcow.conf` instead of fixed port numbers 
- reformat output
2023-03-03 12:34:23 +01:00
Niklas Meyer
c54750ef8b Merge pull request #5085 from kritzl/patch-2
Fix cursor style when hovering 'Aliases' tab
2023-03-03 12:09:14 +01:00
Niklas Meyer
510ef5196b Merge pull request #5097 from rekup/fix/URLHAUS_ABUSE_CH
fix URLHAUS_ABUSE_CH check
2023-03-03 12:04:07 +01:00
FreddleSpl0it
04e46f9f5b [Imapsync] Use pure perl code for XOAUTH2 authmech 2023-03-03 09:57:09 +01:00
milkmaker
6c0a5028c0 [Web] Updated lang.da-dk.json (#5102)
Co-authored-by: Tacaly <frederick@tacaly.com>
2023-03-02 20:02:08 +01:00
Niklas Meyer
791bbeeb39 Merge pull request #5098 from mailcow/feat/fix-raw-attr
Add raw attribute for lang.admin.hash_remove_info
2023-03-01 21:36:40 +01:00
Peter
a5b8f1b7f7 Update to PHP 8.2 2023-02-28 20:08:33 +01:00
Peter
af267ff706 Add raw attribute for lang.admin.hash_remove_info 2023-02-28 19:42:46 +01:00
Reto Kupferschmid
46cc022590 fix URLHAUS_ABUSE_CH check 2023-02-28 14:30:38 +01:00
Bruno Léon
f77c65411d Fix SNAT never being added because of exception
Some firewall rule object (iptc) do not have a parameter
attribute, which results in an exception being triggered,
and the mailcow SNAT rule to never be created.

Firewall rules that trigger such exception are:
- -A POSTROUTING -s 192.168.122.0/24 -d 224.0.0.0/24 -j RETURN

This commit just verify attribute presence, and skip the rule
properly instead of triggering an exception.
2023-02-27 12:04:32 +01:00
milkmaker
1052e13af8 Translations update from Weblate (#5092)
* [Web] Updated lang.da-dk.json

Co-authored-by: Tacaly <frederick@tacaly.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.pl-pl.json

Co-authored-by: KristopherMackowiak <kkriss75@gmail.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

---------

Co-authored-by: Tacaly <frederick@tacaly.com>
Co-authored-by: KristopherMackowiak <kkriss75@gmail.com>
2023-02-25 19:25:24 +01:00
Niklas Meyer
11e1502b12 Merge pull request #5089 from mailcow/renovate/nextcloud-server-25.x 2023-02-24 10:53:12 +01:00
renovate[bot]
02afc45a15 Update dependency nextcloud/server to v25.0.4
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-02-23 20:07:37 +00:00
kritzl
3e1cfe0d08 Fix cursor style when hovering 'Aliases' tab 2023-02-22 00:11:56 +01:00
46 changed files with 488 additions and 403 deletions

View File

@@ -14,7 +14,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Mark/Close Stale Issues and Pull Requests 🗑️ - name: Mark/Close Stale Issues and Pull Requests 🗑️
uses: actions/stale@v7.0.0 uses: actions/stale@v8.0.0
with: with:
repo-token: ${{ secrets.STALE_ACTION_PAT }} repo-token: ${{ secrets.STALE_ACTION_PAT }}
days-before-stale: 60 days-before-stale: 60

1
.gitignore vendored
View File

@@ -13,7 +13,6 @@ data/conf/dovecot/acl_anyone
data/conf/dovecot/dovecot-master.passwd data/conf/dovecot/dovecot-master.passwd
data/conf/dovecot/dovecot-master.userdb data/conf/dovecot/dovecot-master.userdb
data/conf/dovecot/extra.conf data/conf/dovecot/extra.conf
data/conf/dovecot/dovecot-fts-flatcurve.conf
data/conf/dovecot/global_sieve_* data/conf/dovecot/global_sieve_*
data/conf/dovecot/last_login data/conf/dovecot/last_login
data/conf/dovecot/lua data/conf/dovecot/lua

View File

@@ -380,7 +380,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

View File

@@ -1,17 +1,27 @@
FROM debian:bullseye-slim as build FROM debian:bullseye-slim
LABEL maintainer "Andre Peters <andre.peters@servercow.de>" LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ARG DOVECOT=2.3.19.1 # renovate: datasource=github-tags depName=dovecot/core versioning=semver-coerced
ARG FLATCURVE=v0.3.2 ARG DOVECOT=2.3.20
ARG XAPIAN=1.4.21 # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced
ARG GOSU_VERSION=1.16
ENV LC_ALL C ENV LC_ALL C
# Add groups and users before installing Dovecot to not break compatibility # Add groups and users before installing Dovecot to not break compatibility
RUN touch /etc/default/locale \ RUN groupadd -g 5000 vmail \
&& groupadd -g 401 dovecot \
&& groupadd -g 402 dovenull \
&& groupadd -g 999 sogo \
&& usermod -a -G sogo nobody \
&& useradd -g vmail -u 5000 vmail -d /var/vmail \
&& useradd -c "Dovecot unprivileged user" -d /dev/null -u 401 -g dovecot -s /bin/false dovecot \
&& useradd -c "Dovecot login user" -d /dev/null -u 402 -g dovenull -s /bin/false dovenull \
&& touch /etc/default/locale \
&& apt-get update \ && apt-get update \
&& apt-get -y --no-install-recommends install \ && apt-get -y --no-install-recommends install \
build-essential \
apt-transport-https \ apt-transport-https \
ca-certificates \ ca-certificates \
cpanminus \ cpanminus \
@@ -52,6 +62,7 @@ RUN touch /etc/default/locale \
libproc-processtable-perl \ libproc-processtable-perl \
libreadonly-perl \ libreadonly-perl \
libregexp-common-perl \ libregexp-common-perl \
libssl-dev \
libsys-meminfo-perl \ libsys-meminfo-perl \
libterm-readkey-perl \ libterm-readkey-perl \
libtest-deep-perl \ libtest-deep-perl \
@@ -67,13 +78,7 @@ RUN touch /etc/default/locale \
libunicode-string-perl \ libunicode-string-perl \
liburi-perl \ liburi-perl \
libwww-perl \ libwww-perl \
libstemmer-dev \
libexttextcat-dev \
libldap-dev \
libghc-bzlib-dev \
lua-sql-mysql \ lua-sql-mysql \
liblz4-dev \
libzstd-dev \
lua-socket \ lua-socket \
mariadb-client \ mariadb-client \
procps \ procps \
@@ -84,152 +89,32 @@ RUN touch /etc/default/locale \
syslog-ng-core \ syslog-ng-core \
syslog-ng-mod-redis \ syslog-ng-mod-redis \
wget \ wget \
git \
bison \
flex \
build-essential \
autoconf \
automake \
libtool \
make \
libxapian-dev \
default-libmysqlclient-dev \
libicu-dev \
zlib1g-dev \
pkg-config \
libsqlite3-dev \
liblua5.3-dev \
&& dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \ && dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \ && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
&& chmod +x /usr/local/bin/gosu \ && chmod +x /usr/local/bin/gosu \
&& gosu nobody true && gosu nobody true \
# && apt-key adv --fetch-keys https://repo.dovecot.org/DOVECOT-REPO-GPG \ && apt-key adv --fetch-keys https://repo.dovecot.org/DOVECOT-REPO-GPG \
# && echo "deb https://repo.dovecot.org/ce-${DOVECOT}/debian/bullseye bullseye main" > /etc/apt/sources.list.d/dovecot.list \ && echo "deb https://repo.dovecot.org/ce-${DOVECOT}/debian/bullseye bullseye main" > /etc/apt/sources.list.d/dovecot.list \
# && apt-get update \ && apt-get update \
# && apt-get -y --no-install-recommends install \ && apt-get -y --no-install-recommends install \
# dovecot-lua \ dovecot-lua \
# dovecot-managesieved \ dovecot-managesieved \
# dovecot-sieve \ dovecot-sieve \
# dovecot-lmtpd \ dovecot-lmtpd \
# dovecot-ldap \ dovecot-ldap \
# dovecot-mysql \ dovecot-mysql \
# dovecot-core \ dovecot-core \
# dovecot-pop3d \ dovecot-pop3d \
# dovecot-imapd \ dovecot-imapd \
# dovecot-dev dovecot-solr \
&& pip3 install mysql-connector-python html2text jinja2 redis \
&& apt-get autoremove --purge -y \
&& apt-get autoclean \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /tmp/* /var/tmp/* /root/.cache/
# imapsync dependencies
RUN cpan Crypt::OpenSSL::PKCS12
RUN cd /tmp && git clone --depth 1 --branch ${DOVECOT} https://github.com/dovecot/core.git dovecot/core && cd dovecot/core \
&& ./autogen.sh \
&& PANDOC=false ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --with-ssldir=/etc/ssl --enable-maintainer-mode --with-sql=yes --with-lua=yes --with-mysql --with-ldap --with-zstd --with-lz4 --with-ssl=openssl --with-notify=inotify --with-bzlib --with-zlib --enable-hardening --with-stemmer --with-textcat --with-icu \
&& make -j6 \
&& make install \
&& make clean
RUN cd /tmp && git clone --depth 1 --branch release-0.5 https://github.com/dovecot/pigeonhole dovecot/pigeonhole && cd dovecot/pigeonhole \
&& ./autogen.sh \
&& ./configure --with-dovecot=/usr/lib/dovecot --with-managesieve\
&& make -j6 \
&& make install \
&& make clean
RUN cd /tmp && wget https://oligarchy.co.uk/xapian/${XAPIAN}/xapian-core-${XAPIAN}.tar.xz && tar xf xapian-core-${XAPIAN}.tar.xz && cd xapian-core-${XAPIAN} \
&& ./configure --prefix=/usr/local/xapian \
&& make -j6 \
&& make install \
&& make clean
RUN cd /tmp && git clone --depth 1 --branch ${FLATCURVE} https://github.com/slusarz/dovecot-fts-flatcurve.git dovecot/flatcurve && cd dovecot/flatcurve \
&& ./autogen.sh \
&& ./configure --with-dovecot=/usr/lib/dovecot \
&& make -j6 \
&& make install \
&& make clean
FROM debian:bullseye-slim
RUN groupadd -g 5000 vmail \
&& groupadd -g 401 dovecot \
&& groupadd -g 402 dovenull \
&& groupadd -g 999 sogo \
&& usermod -a -G sogo nobody \
&& useradd -g vmail -u 5000 vmail -d /var/vmail \
&& useradd -c "Dovecot unprivileged user" -d /dev/null -u 401 -g dovecot -s /bin/false dovecot \
&& useradd -c "Dovecot login user" -d /dev/null -u 402 -g dovenull -s /bin/false dovenull \
&& apt update && apt install lua-socket \
mariadb-client \
libstemmer-dev \
libexttextcat-dev \
libicu-dev \
libxapian-dev \
libsqlite3-dev \
liblua5.3-dev \
lua-sql-mysql \
libldap-dev \
procps \
python3-pip \
redis-server \
supervisor \
syslog-ng \
syslog-ng-core \
syslog-ng-mod-redis \
cpanminus \
curl \
libauthen-ntlm-perl \
libcgi-pm-perl \
libcrypt-openssl-rsa-perl \
libcrypt-ssleay-perl \
libdata-uniqid-perl \
libdbd-mysql-perl \
libdbi-perl \
libdigest-hmac-perl \
libdist-checkconflicts-perl \
libencode-imaputf7-perl \
libfile-copy-recursive-perl \
libfile-tail-perl \
libhtml-parser-perl \
libio-compress-perl \
libio-socket-inet6-perl \
libio-socket-ssl-perl \
libio-tee-perl \
libipc-run-perl \
libjson-webtoken-perl \
liblockfile-simple-perl \
libmail-imapclient-perl \
libmodule-implementation-perl \
libmodule-scandeps-perl \
libnet-ssleay-perl \
libpackage-stash-perl \
libpackage-stash-xs-perl \
libpar-packer-perl \
libparse-recdescent-perl \
libproc-processtable-perl \
libreadonly-perl \
libregexp-common-perl \
libsys-meminfo-perl \
libterm-readkey-perl \
libtest-deep-perl \
libtest-fatal-perl \
libtest-mock-guard-perl \
libtest-mockobject-perl \
libtest-nowarnings-perl \
libtest-pod-perl \
libtest-requires-perl \
libtest-simple-perl \
libtest-warn-perl \
libtry-tiny-perl \
libunicode-string-perl \
liburi-perl \
libwww-perl \
dnsutils \
gettext-base -y --no-install-recommends \
&& pip3 install mysql-connector-python html2text jinja2 redis
COPY --from=build /usr/lib/dovecot /usr/lib/dovecot
COPY --from=build /usr/bin/doveconf /usr/bin/doveconf
COPY --from=build /usr/bin/doveadm /usr/bin/doveadm
COPY --from=build /usr/bin/dovecot-sysreport /usr/bin/dovecot-sysreport
COPY --from=build /usr/sbin/dovecot /usr/sbin/dovecot
COPY --from=build /usr/libexec/dovecot/ /usr/libexec/dovecot/
COPY --from=build /usr/local/bin /usr/local/bin
COPY --from=build /usr/local/xapian/ /usr/local/xapian
COPY trim_logs.sh /usr/local/bin/trim_logs.sh COPY trim_logs.sh /usr/local/bin/trim_logs.sh
COPY clean_q_aged.sh /usr/local/bin/clean_q_aged.sh COPY clean_q_aged.sh /usr/local/bin/clean_q_aged.sh
COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf

View File

@@ -109,19 +109,17 @@ EOF
echo -n ${ACL_ANYONE} > /etc/dovecot/acl_anyone echo -n ${ACL_ANYONE} > /etc/dovecot/acl_anyone
if [[ "${SKIP_XAPIAN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then if [[ "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify listescape replication' > /etc/dovecot/mail_plugins echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify listescape replication' > /etc/dovecot/mail_plugins
echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify listescape replication mail_log' > /etc/dovecot/mail_plugins_imap echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify listescape replication mail_log' > /etc/dovecot/mail_plugins_imap
echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl notify listescape replication' > /etc/dovecot/mail_plugins_lmtp echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
else else
echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_flatcurve listescape replication' > /etc/dovecot/mail_plugins echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_solr listescape replication' > /etc/dovecot/mail_plugins
echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_flatcurve listescape replication' > /etc/dovecot/mail_plugins_imap echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_solr listescape replication' > /etc/dovecot/mail_plugins_imap
echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_flatcurve notify listescape replication' > /etc/dovecot/mail_plugins_lmtp echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_solr notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
fi fi
chmod 644 /etc/dovecot/mail_plugins /etc/dovecot/mail_plugins_imap /etc/dovecot/mail_plugins_lmtp /templates/quarantine.tpl chmod 644 /etc/dovecot/mail_plugins /etc/dovecot/mail_plugins_imap /etc/dovecot/mail_plugins_lmtp /templates/quarantine.tpl
sed -i 's/vsz_limit.*/vsz_limit = '${XAPIAN_HEAP}m/g /etc/dovecot/dovecot-fts-flatcurve.conf
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-userdb.conf cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-userdb.conf
# Autogenerated by mailcow # Autogenerated by mailcow
driver = mysql driver = mysql

View File

@@ -8492,6 +8492,7 @@ sub xoauth2
require HTML::Entities ; require HTML::Entities ;
require JSON ; require JSON ;
require JSON::WebToken::Crypt::RSA ; require JSON::WebToken::Crypt::RSA ;
require Crypt::OpenSSL::PKCS12;
require Crypt::OpenSSL::RSA ; require Crypt::OpenSSL::RSA ;
require Encode::Byte ; require Encode::Byte ;
require IO::Socket::SSL ; require IO::Socket::SSL ;
@@ -8532,8 +8533,9 @@ sub xoauth2
$sync->{ debug } and myprint( "Service account: $iss\nKey file: $keyfile\nKey password: $keypass\n"); $sync->{ debug } and myprint( "Service account: $iss\nKey file: $keyfile\nKey password: $keypass\n");
# Get private key from p12 file (would be better in perl...) # Get private key from p12 file
$key = `openssl pkcs12 -in "$keyfile" -nodes -nocerts -passin pass:$keypass -nomacver`; my $pkcs12 = Crypt::OpenSSL::PKCS12->new_from_file($keyfile);
$key = $pkcs12->private_key($keypass);
$sync->{ debug } and myprint( "Private key:\n$key\n"); $sync->{ debug } and myprint( "Private key:\n$key\n");
} }

View File

@@ -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
@@ -332,7 +342,7 @@ def watch():
logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data'])) logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data']))
ban(addr) ban(addr)
except Exception as ex: except Exception as ex:
logWarn('Error reading log line from pubsub') logWarn('Error reading log line from pubsub: %s' % ex)
quit_now = True quit_now = True
exit_code = 2 exit_code = 2
@@ -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):

View File

@@ -1,4 +1,4 @@
FROM php:8.1-fpm-alpine3.17 FROM php:8.2-fpm-alpine3.17
LABEL maintainer "Andre Peters <andre.peters@servercow.de>" LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced # renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced
@@ -12,7 +12,7 @@ ARG MEMCACHED_PECL_VERSION=3.2.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced # renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced
ARG REDIS_PECL_VERSION=5.3.7 ARG REDIS_PECL_VERSION=5.3.7
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced # renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced
ARG COMPOSER_VERSION=2.5.4 ARG COMPOSER_VERSION=2.5.5
RUN apk add -U --no-cache autoconf \ RUN apk add -U --no-cache autoconf \
aspell-dev \ aspell-dev \
@@ -52,6 +52,7 @@ RUN apk add -U --no-cache autoconf \
libxpm-dev \ libxpm-dev \
libzip \ libzip \
libzip-dev \ libzip-dev \
linux-headers \
make \ make \
mysql-client \ mysql-client \
openldap-dev \ openldap-dev \
@@ -75,7 +76,7 @@ RUN apk add -U --no-cache autoconf \
--with-webp \ --with-webp \
--with-xpm \ --with-xpm \
--with-avif \ --with-avif \
&& docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets zip bcmath gmp \ && docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets sysvsem zip bcmath gmp \
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \ && docker-php-ext-configure imap --with-imap --with-imap-ssl \
&& docker-php-ext-install -j 4 imap \ && docker-php-ext-install -j 4 imap \
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \ && curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \
@@ -99,6 +100,7 @@ RUN apk add -U --no-cache autoconf \
libxml2-dev \ libxml2-dev \
libxpm-dev \ libxpm-dev \
libzip-dev \ libzip-dev \
linux-headers \
make \ make \
openldap-dev \ openldap-dev \
pcre-dev \ pcre-dev \

View File

@@ -24,7 +24,7 @@ server {
add_header X-Download-Options "noopen" always; add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always; add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always; add_header X-Robots-Tag "noindex, nofollow" always;
add_header X-XSS-Protection "1; mode=block" always; add_header X-XSS-Protection "1; mode=block" always;
fastcgi_hide_header X-Powered-By; fastcgi_hide_header X-Powered-By;

View File

@@ -1,23 +0,0 @@
plugin {
fts = flatcurve
fts_autoindex = yes
fts_languages = en de
fts_tokenizers = generic email-address
fts_tokenizer_generic = algorithm=simple
# All of these are optional, and indicate the default values.
# They are listed here for documentation purposes; most people should
# not need to define/override in their config.
fts_flatcurve_commit_limit = 500
fts_flatcurve_max_term_size = 30
fts_flatcurve_min_term_size = 2
fts_flatcurve_optimize_limit = 10
fts_flatcurve_rotate_size = 5000
fts_flatcurve_rotate_time = 5000
fts_flatcurve_substring_search = yes
}
service indexer-worker {
vsz_limit = 1024m
}

View File

@@ -11,7 +11,6 @@ auth_mechanisms = plain login
#mail_debug = yes #mail_debug = yes
#auth_debug = yes #auth_debug = yes
log_path = syslog log_path = syslog
log_debug = category=fts-flatcurve
disable_plaintext_auth = yes disable_plaintext_auth = yes
# Uncomment on NFS share # Uncomment on NFS share
#mmap_disable = yes #mmap_disable = yes
@@ -190,6 +189,9 @@ plugin {
acl_shared_dict = file:/var/vmail/shared-mailboxes.db acl_shared_dict = file:/var/vmail/shared-mailboxes.db
acl = vfile acl = vfile
acl_user = %u acl_user = %u
fts = solr
fts_autoindex = yes
fts_solr = url=http://solr:8983/solr/dovecot-fts/
quota = dict:Userquota::proxy::sqlquota quota = dict:Userquota::proxy::sqlquota
quota_rule2 = Trash:storage=+100%% quota_rule2 = Trash:storage=+100%%
sieve = /var/vmail/sieve/%u.sieve sieve = /var/vmail/sieve/%u.sieve
@@ -240,7 +242,6 @@ plugin {
mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
mail_log_fields = uid box msgid size mail_log_fields = uid box msgid size
mail_log_cached_only = yes mail_log_cached_only = yes
} }
service quota-warning { service quota-warning {
executable = script /usr/local/bin/quota_notify.py executable = script /usr/local/bin/quota_notify.py
@@ -296,7 +297,6 @@ replication_dsync_parameters = -d -l 30 -U -n INBOX
!include_try /etc/dovecot/extra.conf !include_try /etc/dovecot/extra.conf
!include_try /etc/dovecot/sogo-sso.conf !include_try /etc/dovecot/sogo-sso.conf
!include_try /etc/dovecot/shared_namespace.conf !include_try /etc/dovecot/shared_namespace.conf
!include_try /etc/dovecot/dovecot-fts-flatcurve.conf
# </Includes> # </Includes>
default_client_limit = 10400 default_client_limit = 10400
default_vsz_limit = 1024 M default_vsz_limit = 1024 M

View File

@@ -8,7 +8,7 @@ VIRUS_FOUND {
} }
# Bad policy from free mail providers # Bad policy from free mail providers
FREEMAIL_POLICY_FAILURE { FREEMAIL_POLICY_FAILURE {
expression = "-g+:policies & !DMARC_POLICY_ALLOW & !MAILLIST & ( FREEMAIL_ENVFROM | FREEMAIL_FROM ) & !WHITELISTED_FWD_HOST"; expression = "FREEMAIL_FROM & !DMARC_POLICY_ALLOW & !MAILLIST& !WHITELISTED_FWD_HOST & -g+:policies";
score = 16.0; score = 16.0;
} }
# Applies to freemail with undisclosed recipients # Applies to freemail with undisclosed recipients

View File

@@ -159,8 +159,8 @@ BAZAAR_ABUSE_CH {
} }
URLHAUS_ABUSE_CH { URLHAUS_ABUSE_CH {
type = "url"; type = "selector";
filter = "full"; selector = "urls";
map = "https://urlhaus.abuse.ch/downloads/text_online/"; map = "https://urlhaus.abuse.ch/downloads/text_online/";
score = 10.0; score = 10.0;
} }

View File

@@ -62,7 +62,7 @@
SOGoFirstDayOfWeek = "1"; SOGoFirstDayOfWeek = "1";
SOGoSieveFolderEncoding = "UTF-8"; SOGoSieveFolderEncoding = "UTF-8";
SOGoPasswordChangeEnabled = YES; SOGoPasswordChangeEnabled = NO;
SOGoSentFolderName = "Sent"; SOGoSentFolderName = "Sent";
SOGoMailShowSubscribedFoldersOnly = NO; SOGoMailShowSubscribedFoldersOnly = NO;
NGImap4ConnectionStringSeparator = "/"; NGImap4ConnectionStringSeparator = "/";

View File

@@ -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:

View File

@@ -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;
} }

View File

@@ -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;
}

View File

@@ -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;
} }

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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") {
@@ -5053,12 +5058,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 +5272,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();
} }
} }

View File

@@ -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>';
} }
}, },
{ {

View File

@@ -1181,7 +1181,7 @@ jQuery(function($){
if (table = $('#' + log_table).DataTable()) { if (table = $('#' + log_table).DataTable()) {
var heading = $('#' + log_table).closest('.card').find('.card-header'); var heading = $('#' + log_table).closest('.card').find('.card-header');
var load_rows = (table.page.len() + 1) + '-' + (table.page.len() + new_nrows) var load_rows = (table.data().length + 1) + '-' + (table.data().length + new_nrows)
$.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){ $.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; } if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }

View File

@@ -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&&'&#8212;'); 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&&'&#8212;');
} }
}, },
{ {
@@ -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&&'&#8212;'); 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&&'&#8212;');
} }
}, },
{ {
@@ -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&&'&#8212;'); 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&&'&#8212;');
} }
}, },
{ {
@@ -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&&'&#8212;'); 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&&'&#8212;');
} }
}, },
{ {
@@ -1578,7 +1577,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&&'&#8212;'); 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&&'&#8212;');
} }
}, },
{ {
@@ -1675,7 +1674,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 +1781,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 +1916,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 +1935,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 +1951,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 +2034,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 +2170,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>';
} }
}, },
{ {

View File

@@ -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": "&#10003;" "yes": "&#10003;",
"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",

View File

@@ -4,15 +4,15 @@
"app_passwds": "Administrer app-adgangskoder", "app_passwds": "Administrer app-adgangskoder",
"bcc_maps": "BCC kort", "bcc_maps": "BCC kort",
"delimiter_action": "Afgrænsning handling", "delimiter_action": "Afgrænsning handling",
"eas_reset": "Nulstil EAS endheder", "eas_reset": "Nulstil EAS enheder",
"extend_sender_acl": "Tillad at udvide afsenderens ACL med eksterne adresser", "extend_sender_acl": "Tillad at udvide afsenderens ACL med eksterne adresser",
"filters": "Filtre", "filters": "Filtre",
"login_as": "Login som mailboks bruger", "login_as": "Login som mailboks bruger",
"prohibited": "Forbudt af ACL", "prohibited": "Nægtet af ACL",
"protocol_access": "Ændre protokol adgang", "protocol_access": "Skift protokol adgang",
"pushover": "Pushover", "pushover": "Pushover",
"quarantine": "Karantæneaktioner", "quarantine": "Karantænehandlinger",
"quarantine_attachments": "Karantæne vedhæftede filer", "quarantine_attachments": "Karantænevedhæftede filer",
"quarantine_notification": "Skift karantænemeddelelser", "quarantine_notification": "Skift karantænemeddelelser",
"ratelimit": "Satsgrænse", "ratelimit": "Satsgrænse",
"recipient_maps": "Modtagerkort", "recipient_maps": "Modtagerkort",
@@ -20,12 +20,15 @@
"sogo_access": "Tillad styring af SOGo-adgang", "sogo_access": "Tillad styring af SOGo-adgang",
"sogo_profile_reset": "Nulstil SOGo-profil", "sogo_profile_reset": "Nulstil SOGo-profil",
"spam_alias": "Midlertidige aliasser", "spam_alias": "Midlertidige aliasser",
"spam_policy": "Sortliste / hvidliste", "spam_policy": "Sortliste/hvidliste",
"spam_score": "Spam-score", "spam_score": "Spam-score",
"syncjobs": "Synkroniserings job", "syncjobs": "Synkroniserings job",
"tls_policy": "TLS politik", "tls_policy": "TLS politik",
"unlimited_quota": "Ubegrænset plads for mailbokse", "unlimited_quota": "Ubegrænset plads for mailbokse",
"domain_desc": "Skift domæne beskrivelse" "domain_desc": "Skift domæne beskrivelse",
"domain_relayhost": "Skift relæ host for et domæne",
"mailbox_relayhost": "Skift relæ-host for en postkasse",
"quarantine_category": "Skift kategorien for karantænemeddelelse"
}, },
"add": { "add": {
"activate_filter_warn": "Alle andre filtre deaktiveres, når aktiv er markeret.", "activate_filter_warn": "Alle andre filtre deaktiveres, når aktiv er markeret.",
@@ -59,7 +62,7 @@
"gal": "Global adresseliste", "gal": "Global adresseliste",
"gal_info": "GAL indeholder alle objekter i et domæne og kan ikke redigeres af nogen bruger. Information om ledig / optaget i SOGo mangler, hvis deaktiveret! <b> Genstart SOGo for at anvende ændringer. </b>", "gal_info": "GAL indeholder alle objekter i et domæne og kan ikke redigeres af nogen bruger. Information om ledig / optaget i SOGo mangler, hvis deaktiveret! <b> Genstart SOGo for at anvende ændringer. </b>",
"generate": "generere", "generate": "generere",
"goto_ham": "Lær som <span class=\"text-success\"><b>ham</b></span>", "goto_ham": "Lær som <span class=\"text-success\"><b>ønsket</b></span>",
"goto_null": "Kassér e-mail i stilhed", "goto_null": "Kassér e-mail i stilhed",
"goto_spam": "Lær som <span class=\"text-danger\"><b>spam</b></span>", "goto_spam": "Lær som <span class=\"text-danger\"><b>spam</b></span>",
"hostname": "Vært", "hostname": "Vært",
@@ -80,7 +83,7 @@
"private_comment": "Privat kommentar", "private_comment": "Privat kommentar",
"public_comment": "Offentlig kommentar", "public_comment": "Offentlig kommentar",
"quota_mb": "Kvota (Mb)", "quota_mb": "Kvota (Mb)",
"relay_all": "Send alle modtagere videre", "relay_all": "Besvar alle modtager",
"relay_all_info": "↪ Hvis du vælger <b> ikke </b> at videresende alle modtagere, skal du tilføje et (\"blind\") postkasse til hver enkelt modtager, der skal videresendes.", "relay_all_info": "↪ Hvis du vælger <b> ikke </b> at videresende alle modtagere, skal du tilføje et (\"blind\") postkasse til hver enkelt modtager, der skal videresendes.",
"relay_domain": "Send dette domæne videre", "relay_domain": "Send dette domæne videre",
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Du kan definere transportkort til en tilpasset destination for dette domæne. Hvis ikke indstillet, foretages der et MX-opslag.", "relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Du kan definere transportkort til en tilpasset destination for dette domæne. Hvis ikke indstillet, foretages der et MX-opslag.",
@@ -101,7 +104,10 @@
"timeout2": "Timeout for forbindelse til lokal vært", "timeout2": "Timeout for forbindelse til lokal vært",
"username": "Brugernavn", "username": "Brugernavn",
"validate": "Bekræft", "validate": "Bekræft",
"validation_success": "Valideret med succes" "validation_success": "Valideret med succes",
"bcc_dest_format": "BCC-destination skal være en enkelt gyldig e-mail-adresse.<br>Hvis du har brug for at sende en kopi til flere adresser, kan du oprette et alias og bruge det her.",
"app_passwd_protocols": "Tilladte protokoller for app adgangskode",
"tags": "Tag's"
}, },
"admin": { "admin": {
"access": "Adgang", "access": "Adgang",
@@ -308,7 +314,10 @@
"username": "Brugernavn", "username": "Brugernavn",
"validate_license_now": "Valider GUID mod licensserver", "validate_license_now": "Valider GUID mod licensserver",
"verify": "Verificere", "verify": "Verificere",
"yes": "&#10003;" "yes": "&#10003;",
"ip_check_opt_in": "Opt-In for brug af tredjepartstjeneste <strong>ipv4.mailcow.email</strong> og <strong>ipv6.mailcow.email</strong> til at finde eksterne IP-adresser.",
"queue_unban": "unban",
"admins": "Administratorer"
}, },
"danger": { "danger": {
"access_denied": "Adgang nægtet eller ugyldig formular data", "access_denied": "Adgang nægtet eller ugyldig formular data",
@@ -425,7 +434,8 @@
"username_invalid": "Brugernavn %s kan ikke bruges", "username_invalid": "Brugernavn %s kan ikke bruges",
"validity_missing": "Tildel venligst en gyldighedsperiode", "validity_missing": "Tildel venligst en gyldighedsperiode",
"value_missing": "Angiv alle værdier", "value_missing": "Angiv alle værdier",
"yotp_verification_failed": "Yubico OTP verifikationen mislykkedes: %s" "yotp_verification_failed": "Yubico OTP verifikationen mislykkedes: %s",
"webauthn_publickey_failed": "Der er ikke gemt nogen offentlig nøgle for den valgte autentifikator"
}, },
"debug": { "debug": {
"chart_this_server": "Diagram (denne server)", "chart_this_server": "Diagram (denne server)",
@@ -442,7 +452,8 @@
"solr_status": "Solr-status", "solr_status": "Solr-status",
"started_on": "Startede den", "started_on": "Startede den",
"static_logs": "Statiske logfiler", "static_logs": "Statiske logfiler",
"system_containers": "System og Beholdere" "system_containers": "System og Beholdere",
"error_show_ip": "Kunne ikke finde de offentlige IP-adresser"
}, },
"diagnostics": { "diagnostics": {
"cname_from_a": "Værdi afledt af A / AAAA-post. Dette understøttes, så længe posten peger på den korrekte ressource.", "cname_from_a": "Værdi afledt af A / AAAA-post. Dette understøttes, så længe posten peger på den korrekte ressource.",
@@ -553,7 +564,11 @@
"title": "Rediger objekt", "title": "Rediger objekt",
"unchanged_if_empty": "Lad være tomt, hvis uændret", "unchanged_if_empty": "Lad være tomt, hvis uændret",
"username": "Brugernavn", "username": "Brugernavn",
"validate_save": "Valider og gem" "validate_save": "Valider og gem",
"admin": "Rediger administrator",
"lookup_mx": "Destination er et regulært udtryk, der matcher MX-navnet (<code>.*google\\.dk</code> for at dirigere al e-mail, der er målrettet til en MX, der ender på google.dk, over dette hop)",
"mailbox_relayhost_info": "Anvendt på postkassen og kun direkte aliasser, og overskriver et domæne relæ-host.",
"quota_warning_bcc": "Kvoteadvarsel BCC"
}, },
"footer": { "footer": {
"cancel": "Afbestille", "cancel": "Afbestille",
@@ -571,7 +586,7 @@
"header": { "header": {
"administration": "Konfiguration og detailer", "administration": "Konfiguration og detailer",
"apps": "Apps", "apps": "Apps",
"debug": "Systemoplysninger", "debug": "Information",
"email": "E-Mail", "email": "E-Mail",
"mailcow_config": "Konfiguration", "mailcow_config": "Konfiguration",
"quarantine": "Karantæne", "quarantine": "Karantæne",
@@ -739,7 +754,10 @@
"username": "Brugernavn", "username": "Brugernavn",
"waiting": "Venter", "waiting": "Venter",
"weekly": "Ugentlig", "weekly": "Ugentlig",
"yes": "&#10003;" "yes": "&#10003;",
"goto_ham": "Lær som <b>ønsket</b>",
"catch_all": "Fang-alt",
"open_logs": "Åben logfiler"
}, },
"oauth2": { "oauth2": {
"access_denied": "Log ind som mailboks ejer for at give adgang via OAuth2.", "access_denied": "Log ind som mailboks ejer for at give adgang via OAuth2.",
@@ -1030,7 +1048,7 @@
"spamfilter_table_empty": "Intet data at vise", "spamfilter_table_empty": "Intet data at vise",
"spamfilter_table_remove": "slet", "spamfilter_table_remove": "slet",
"spamfilter_table_rule": "Regl", "spamfilter_table_rule": "Regl",
"spamfilter_wl": "Hvisliste", "spamfilter_wl": "Hvidliste",
"spamfilter_wl_desc": "Hvidlistede e-mail-adresser til <b>aldrig</b> at klassificeres som spam. Wildcards kan bruges. Et filter anvendes kun på direkte aliaser (aliaser med en enkelt målpostkasse) eksklusive catch-aliaser og selve en postkasse.", "spamfilter_wl_desc": "Hvidlistede e-mail-adresser til <b>aldrig</b> at klassificeres som spam. Wildcards kan bruges. Et filter anvendes kun på direkte aliaser (aliaser med en enkelt målpostkasse) eksklusive catch-aliaser og selve en postkasse.",
"spamfilter_yellow": "Gul: denne besked kan være spam, vil blive tagget som spam og flyttes til din junk-mappe", "spamfilter_yellow": "Gul: denne besked kan være spam, vil blive tagget som spam og flyttes til din junk-mappe",
"status": "Status", "status": "Status",
@@ -1066,5 +1084,11 @@
"quota_exceeded_scope": "Domænekvote overskredet: Kun ubegrænsede postkasser kan oprettes i dette domæneomfang.", "quota_exceeded_scope": "Domænekvote overskredet: Kun ubegrænsede postkasser kan oprettes i dette domæneomfang.",
"session_token": "Form nøgle ugyldig: Nøgle passer ikke", "session_token": "Form nøgle ugyldig: Nøgle passer ikke",
"session_ua": "Form nøgle ugyldig: Bruger-Agent gyldighedskontrols fejl" "session_ua": "Form nøgle ugyldig: Bruger-Agent gyldighedskontrols fejl"
},
"datatables": {
"lengthMenu": "Vis _MENU_ poster",
"paginate": {
"first": "Først"
}
} }
} }

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -24,7 +24,7 @@
"spam_policy": "Liste Noire/Liste Blanche", "spam_policy": "Liste Noire/Liste Blanche",
"spam_score": "Score SPAM", "spam_score": "Score SPAM",
"syncjobs": "Tâches de synchronisation", "syncjobs": "Tâches de synchronisation",
"tls_policy": "Police TLS", "tls_policy": "Politique TLS",
"unlimited_quota": "Quota illimité pour les boites de courriel", "unlimited_quota": "Quota illimité pour les boites de courriel",
"domain_desc": "Modifier la description du domaine", "domain_desc": "Modifier la description du domaine",
"domain_relayhost": "Changer le relais pour un domaine", "domain_relayhost": "Changer le relais pour un domaine",
@@ -106,7 +106,8 @@
"validate": "Valider", "validate": "Valider",
"validation_success": "Validation réussie", "validation_success": "Validation réussie",
"bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici.", "bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici.",
"tags": "Etiquettes" "tags": "Etiquettes",
"app_passwd_protocols": "Protocoles autorisés pour le mot de passe de l'application"
}, },
"admin": { "admin": {
"access": "Accès", "access": "Accès",
@@ -171,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",
@@ -321,7 +324,9 @@
"admins": "Administrateurs", "admins": "Administrateurs",
"api_read_only": "Accès lecture-seule", "api_read_only": "Accès lecture-seule",
"password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules", "password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules",
"password_policy_numbers": "Doit contenir au moins un chiffre" "password_policy_numbers": "Doit contenir au moins un chiffre",
"ip_check": "Vérification IP",
"ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>"
}, },
"danger": { "danger": {
"access_denied": "Accès refusé ou données de formulaire non valides", "access_denied": "Accès refusé ou données de formulaire non valides",
@@ -440,7 +445,12 @@
"username_invalid": "Le nom d'utilisateur %s ne peut pas être utilisé", "username_invalid": "Le nom d'utilisateur %s ne peut pas être utilisé",
"validity_missing": "Veuillez attribuer une période de validité", "validity_missing": "Veuillez attribuer une période de validité",
"value_missing": "Veuillez fournir toutes les valeurs", "value_missing": "Veuillez fournir toutes les valeurs",
"yotp_verification_failed": "La vérification Yubico OTP a échoué : %s" "yotp_verification_failed": "La vérification Yubico OTP a échoué : %s",
"webauthn_authenticator_failed": "L'authentificateur selectionné est introuvable",
"demo_mode_enabled": "Le mode de démonstration est activé",
"template_exists": "La template %s existe déja",
"template_id_invalid": "Le numéro de template %s est invalide",
"template_name_invalid": "Le nom de la template est invalide"
}, },
"debug": { "debug": {
"chart_this_server": "Graphique (ce serveur)", "chart_this_server": "Graphique (ce serveur)",
@@ -578,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": {
@@ -1081,9 +1091,12 @@
"username": "Nom d'utilisateur", "username": "Nom d'utilisateur",
"verify": "Vérification", "verify": "Vérification",
"waiting": "En attente", "waiting": "En attente",
"week": "Semaine", "week": "semaine",
"weekly": "Hebdomadaire", "weekly": "Hebdomadaire",
"weeks": "semaines" "weeks": "semaines",
"months": "mois",
"year": "année",
"years": "années"
}, },
"warning": { "warning": {
"cannot_delete_self": "Impossible de supprimer lutilisateur connecté", "cannot_delete_self": "Impossible de supprimer lutilisateur connecté",

View File

@@ -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",

View File

@@ -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",

View File

@@ -1,7 +1,8 @@
{ {
"acl": { "acl": {
"sogo_profile_reset": "Usuń profil SOGo (webmail)", "sogo_profile_reset": "Usuń profil SOGo (webmail)",
"syncjobs": "Polecenie synchronizacji" "syncjobs": "Polecenie synchronizacji",
"alias_domains": "Dodaj aliasy domen"
}, },
"add": { "add": {
"active": "Aktywny", "active": "Aktywny",

View File

@@ -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;
} }
} }

View File

@@ -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>

View File

@@ -612,7 +612,7 @@
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="rl_log" data-table="rl_log" href="#">{{ lang.datatables.expand_all }}</a></li> <li class="table_collapse_option"><a class="dropdown-item" data-datatables-expand="rl_log" data-table="rl_log" href="#">{{ lang.datatables.expand_all }}</a></li>
<li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="rl_log" data-table="rl_log" href="#">{{ lang.datatables.collapse_all }}</a></li> <li class="table_collapse_option"><a class="dropdown-item" data-datatables-collapse="rl_log" data-table="rl_log" href="#">{{ lang.datatables.collapse_all }}</a></li>
</ul> </ul>
<p class="text-muted">{{ lang.admin.hash_remove_info }}</p> <p class="text-muted">{{ lang.admin.hash_remove_info|raw }}</p>
<table id="rl_log" class="table table-striped dt-responsive w-100"></table> <table id="rl_log" class="table table-striped dt-responsive w-100"></table>
</div> </div>
</div> </div>

View File

@@ -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"

View File

@@ -19,7 +19,7 @@
</li> </li>
<li class="nav-item" role="presentation"><button class="nav-link" aria-controls="tab-resources" role="tab" data-bs-toggle="tab" data-bs-target="#tab-resources">{{ lang.mailbox.resources }}</button></li> <li class="nav-item" role="presentation"><button class="nav-link" aria-controls="tab-resources" role="tab" data-bs-toggle="tab" data-bs-target="#tab-resources">{{ lang.mailbox.resources }}</button></li>
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" data-bs-target="#">{{ lang.mailbox.aliases }}</a> <a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.aliases }}</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li role="presentation"><button class="dropdown-item" aria-selected="false" aria-controls="tab-mbox-aliases" role="tab" data-bs-toggle="tab" data-bs-target="#tab-mbox-aliases">{{ lang.mailbox.aliases }}</button></li> <li role="presentation"><button class="dropdown-item" aria-selected="false" aria-controls="tab-mbox-aliases" role="tab" data-bs-toggle="tab" data-bs-target="#tab-mbox-aliases">{{ lang.mailbox.aliases }}</button></li>
<li role="presentation"><button class="dropdown-item" aria-selected="false" aria-controls="tab-domain-aliases" role="tab" data-bs-toggle="tab" data-bs-target="#tab-domain-aliases">{{ lang.mailbox.domain_aliases }}</button></li> <li role="presentation"><button class="dropdown-item" aria-selected="false" aria-controls="tab-domain-aliases" role="tab" data-bs-toggle="tab" data-bs-target="#tab-domain-aliases">{{ lang.mailbox.domain_aliases }}</button></li>

View File

@@ -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>

View File

@@ -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"

View File

@@ -106,7 +106,7 @@ services:
- rspamd - rspamd
php-fpm-mailcow: php-fpm-mailcow:
image: mailcow/phpfpm:1.82 image: mailcow/phpfpm:1.83
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
@@ -154,7 +154,7 @@ services:
- API_KEY_READ_ONLY=${API_KEY_READ_ONLY:-invalid} - API_KEY_READ_ONLY=${API_KEY_READ_ONLY:-invalid}
- API_ALLOW_FROM=${API_ALLOW_FROM:-invalid} - API_ALLOW_FROM=${API_ALLOW_FROM:-invalid}
- COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized} - COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized}
- SKIP_XAPIAN=${SKIP_XAPIAN:-y} - SKIP_SOLR=${SKIP_SOLR:-y}
- SKIP_CLAMD=${SKIP_CLAMD:-n} - SKIP_CLAMD=${SKIP_CLAMD:-n}
- SKIP_SOGO=${SKIP_SOGO:-n} - SKIP_SOGO=${SKIP_SOGO:-n}
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
@@ -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}
@@ -191,7 +191,7 @@ services:
volumes: volumes:
- ./data/hooks/sogo:/hooks:Z - ./data/hooks/sogo:/hooks:Z
- ./data/conf/sogo/:/etc/sogo/:z - ./data/conf/sogo/:/etc/sogo/:z
- ./data/web/inc/init_db.inc.php:/init_db.inc.php:Z - ./data/web/inc/init_db.inc.php:/init_db.inc.php:z
- ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z - ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z
- ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z - ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z
- ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z - ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z
@@ -216,7 +216,7 @@ services:
- sogo - sogo
dovecot-mailcow: dovecot-mailcow:
image: mailcow/dovecot:1.20-xapian image: mailcow/dovecot:1.23
depends_on: depends_on:
- mysql-mailcow - mysql-mailcow
dns: dns:
@@ -250,8 +250,7 @@ services:
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
- MAILDIR_GC_TIME=${MAILDIR_GC_TIME:-7200} - MAILDIR_GC_TIME=${MAILDIR_GC_TIME:-7200}
- ACL_ANYONE=${ACL_ANYONE:-disallow} - ACL_ANYONE=${ACL_ANYONE:-disallow}
- SKIP_XAPIAN=${SKIP_XAPIAN:-y} - SKIP_SOLR=${SKIP_SOLR:-y}
- XAPIAN_HEAP=${XAPIAN_HEAP:-1024}
- MAILDIR_SUB=${MAILDIR_SUB:-} - MAILDIR_SUB=${MAILDIR_SUB:-}
- MASTER=${MASTER:-y} - MASTER=${MASTER:-y}
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
@@ -282,7 +281,7 @@ services:
ofelia.job-exec.dovecot_sarules.schedule: "@every 24h" ofelia.job-exec.dovecot_sarules.schedule: "@every 24h"
ofelia.job-exec.dovecot_sarules.command: "/bin/bash -c \"/usr/local/bin/sa-rules.sh\"" ofelia.job-exec.dovecot_sarules.command: "/bin/bash -c \"/usr/local/bin/sa-rules.sh\""
ofelia.job-exec.dovecot_fts.schedule: "@every 24h" ofelia.job-exec.dovecot_fts.schedule: "@every 24h"
ofelia.job-exec.dovecot_fts.command: "doveadm fts optimize -A" ofelia.job-exec.dovecot_fts.command: "/usr/bin/curl http://solr:8983/solr/dovecot-fts/update?optimize=true"
ofelia.job-exec.dovecot_repl_health.schedule: "@every 5m" ofelia.job-exec.dovecot_repl_health.schedule: "@every 5m"
ofelia.job-exec.dovecot_repl_health.command: "/bin/bash -c \"/usr/local/bin/gosu vmail /usr/local/bin/repl_health.sh\"" ofelia.job-exec.dovecot_repl_health.command: "/bin/bash -c \"/usr/local/bin/gosu vmail /usr/local/bin/repl_health.sh\""
ulimits: ulimits:
@@ -426,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
@@ -511,7 +510,7 @@ services:
- watchdog - watchdog
dockerapi-mailcow: dockerapi-mailcow:
image: mailcow/dockerapi:2.01 image: mailcow/dockerapi:2.03
security_opt: security_opt:
- label=disable - label=disable
restart: always restart: always
@@ -529,6 +528,22 @@ services:
aliases: aliases:
- dockerapi - dockerapi
solr-mailcow:
image: mailcow/solr:1.8.1
restart: always
volumes:
- solr-vol-1:/opt/solr/server/solr/dovecot-fts/data
ports:
- "${SOLR_PORT:-127.0.0.1:18983}:8983"
environment:
- TZ=${TZ}
- SOLR_HEAP=${SOLR_HEAP:-1024}
- SKIP_SOLR=${SKIP_SOLR:-y}
networks:
mailcow-network:
aliases:
- solr
olefy-mailcow: olefy-mailcow:
image: mailcow/olefy:1.11 image: mailcow/olefy:1.11
restart: always restart: always
@@ -584,6 +599,7 @@ services:
- netfilter-mailcow - netfilter-mailcow
- watchdog-mailcow - watchdog-mailcow
- dockerapi-mailcow - dockerapi-mailcow
- solr-mailcow
environment: environment:
- TZ=${TZ} - TZ=${TZ}
image: robbertkl/ipv6nat image: robbertkl/ipv6nat
@@ -615,6 +631,7 @@ volumes:
mysql-socket-vol-1: mysql-socket-vol-1:
redis-vol-1: redis-vol-1:
rspamd-vol-1: rspamd-vol-1:
solr-vol-1:
postfix-vol-1: postfix-vol-1:
crypt-vol-1: crypt-vol-1:
sogo-web-vol-1: sogo-web-vol-1:

View File

@@ -122,23 +122,23 @@ else
fi fi
if [ ${MEM_TOTAL} -le "2097152" ]; then if [ ${MEM_TOTAL} -le "2097152" ]; then
echo "Disabling Xapian (full text search, build in Dovecot) on low-memory system." echo "Disabling Solr on low-memory system."
SKIP_XAPIAN=y SKIP_SOLR=y
elif [ ${MEM_TOTAL} -le "3670016" ]; then elif [ ${MEM_TOTAL} -le "3670016" ]; then
echo "Installed memory is <= 3.5 GiB. We suggest you to disable Xapian (full text search, build in Dovecot) to prevent out-of-memory situations." echo "Installed memory is <= 3.5 GiB. It is recommended to disable Solr to prevent out-of-memory situations."
echo "The default Xapian heap size is 1024 MiB and should be set in mailcow.conf according to your expected load." echo "Solr is a prone to run OOM and should be monitored. The default Solr heap size is 1024 MiB and should be set in mailcow.conf according to your expected load."
echo "Xapian can be re-enabled by setting SKIP_XAPIAN=n in mailcow.conf but will refuse to start with less than 2 GB total memory." echo "Solr can be re-enabled by setting SKIP_SOLR=n in mailcow.conf but will refuse to start with less than 2 GB total memory."
read -r -p "Do you want to disable the FTS Xapian now? [Y/n] " response read -r -p "Do you want to disable Solr now? [Y/n] " response
case $response in case $response in
[nN][oO]|[nN]) [nN][oO]|[nN])
SKIP_XAPIAN=n SKIP_SOLR=n
;; ;;
*) *)
SKIP_XAPIAN=y SKIP_SOLR=y
;; ;;
esac esac
else else
SKIP_XAPIAN=n SKIP_SOLR=n
fi fi
if [[ ${SKIP_BRANCH} != y ]]; then if [[ ${SKIP_BRANCH} != y ]]; then
@@ -205,8 +205,8 @@ DBUSER=mailcow
# Please use long, random alphanumeric strings (A-Za-z0-9) # Please use long, random alphanumeric strings (A-Za-z0-9)
DBPASS=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 | head -c 28) DBPASS=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
DBROOT=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 | head -c 28) DBROOT=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
# ------------------------------ # ------------------------------
# HTTP/S Bindings # HTTP/S Bindings
@@ -243,6 +243,7 @@ POPS_PORT=995
SIEVE_PORT=4190 SIEVE_PORT=4190
DOVEADM_PORT=127.0.0.1:19991 DOVEADM_PORT=127.0.0.1:19991
SQL_PORT=127.0.0.1:13306 SQL_PORT=127.0.0.1:13306
SOLR_PORT=127.0.0.1:18983
REDIS_PORT=127.0.0.1:7654 REDIS_PORT=127.0.0.1:7654
# Your timezone # Your timezone
@@ -260,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}
@@ -328,14 +329,14 @@ SKIP_CLAMD=${SKIP_CLAMD}
SKIP_SOGO=n SKIP_SOGO=n
# Skip Xapian (FTS) on low-memory systems or if you do not want to store a readable index of your mails in vmail-index-vol-1. # Skip Solr on low-memory systems or if you do not want to store a readable index of your mails in solr-vol-1.
SKIP_XAPIAN=${SKIP_XAPIAN} SKIP_SOLR=${SKIP_SOLR}
# Xapian heap size in MB, there is no recommendation, please see Xapians docs. # Solr heap size in MB, there is no recommendation, please see Solr docs.
# Xapian is replacing solr completely. It is supposed to be much more efficient in CPU and RAM consumption. # Solr is a prone to run OOM and should be monitored. Unmonitored Solr setups are not recommended.
XAPIAN_HEAP=1024 SOLR_HEAP=1024
# Allow admins to log into SOGo as email user (without any password) # Allow admins to log into SOGo as email user (without any password)

15
helper-scripts/expiry-dates.sh Normal file → Executable file
View File

@@ -3,10 +3,11 @@
[[ -f mailcow.conf ]] && source mailcow.conf [[ -f mailcow.conf ]] && source mailcow.conf
[[ -f ../mailcow.conf ]] && source ../mailcow.conf [[ -f ../mailcow.conf ]] && source ../mailcow.conf
POSTFIX=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:25 -starttls smtp 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2) POSTFIX=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:${SMTP_PORT} -starttls smtp 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2)
DOVECOT=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:143 -starttls imap 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2) DOVECOT=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:${IMAP_PORT} -starttls imap 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2)
NGINX=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:443 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2) NGINX=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:${HTTPS_PORT} 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2)
echo TLS expiry dates:
echo Postfix: ${POSTFIX} echo "TLS expiry dates:"
echo Dovecot: ${DOVECOT} echo "Postfix: ${POSTFIX}"
echo Nginx: ${NGINX} echo "Dovecot: ${DOVECOT}"
echo "Nginx: ${NGINX}"

View File

@@ -19,7 +19,7 @@ read -r -p "Are you sure you want to reset the mailcow administrator account? [y
response=${response,,} # tolower response=${response,,} # tolower
if [[ "$response" =~ ^(yes|y)$ ]]; then if [[ "$response" =~ ^(yes|y)$ ]]; then
echo -e "\nWorking, please wait..." echo -e "\nWorking, please wait..."
random=$(</dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-16}) random=$(</dev/urandom tr -dc _A-Z-a-z-0-9 2> /dev/null | head -c${1:-16})
password=$(docker exec -it $(docker ps -qf name=dovecot-mailcow) doveadm pw -s SSHA256 -p ${random} | tr -d '\r') password=$(docker exec -it $(docker ps -qf name=dovecot-mailcow) doveadm pw -s SSHA256 -p ${random} | tr -d '\r')
docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM admin WHERE username='admin';" docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM admin WHERE username='admin';"
docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM domain_admins WHERE username='admin';" docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM domain_admins WHERE username='admin';"

View File

@@ -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=25.0.3 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"
@@ -122,7 +126,7 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
&& chmod +x ./data/web/nextcloud/occ && chmod +x ./data/web/nextcloud/occ
echo -e "\033[33mCreating 'nextcloud' database...\033[0m" echo -e "\033[33mCreating 'nextcloud' database...\033[0m"
NC_DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28) NC_DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
NC_DBUSER=nextcloud NC_DBUSER=nextcloud
NC_DBNAME=nextcloud NC_DBNAME=nextcloud
@@ -138,7 +142,7 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
echo "" echo ""
echo -e "\033[33mInstalling Nextcloud...\033[0m" echo -e "\033[33mInstalling Nextcloud...\033[0m"
ADMIN_NC_PASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28) ADMIN_NC_PASS=$(</dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
echo -ne "[1/4] Setting correct permissions for www-data" echo -ne "[1/4] Setting correct permissions for www-data"
docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud" docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud"

103
update.sh
View File

@@ -176,18 +176,19 @@ remove_obsolete_nginx_ports() {
} }
detect_docker_compose_command(){ detect_docker_compose_command(){
if ! [ "${DOCKER_COMPOSE_VERSION}" == "native" ] && ! [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
if docker compose > /dev/null 2>&1; then if docker compose > /dev/null 2>&1; then
if docker compose version --short | grep "2." > /dev/null 2>&1; then if docker compose version --short | grep "2." > /dev/null 2>&1; then
DOCKER_COMPOSE_VERSION=native DOCKER_COMPOSE_VERSION=native
COMPOSE_COMMAND="docker compose" COMPOSE_COMMAND="docker compose"
echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m" echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m" echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' $SCRIPT_DIR/mailcow.conf
sleep 2 sleep 2
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://mailcow.github.io/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/mailcow-dockerized-docs/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
@@ -197,26 +198,60 @@ if ! [ "${DOCKER_COMPOSE_VERSION}" == "native" ] && ! [ "${DOCKER_COMPOSE_VERSIO
COMPOSE_COMMAND="docker-compose" COMPOSE_COMMAND="docker-compose"
echo -e "\e[31mFound Docker Compose Standalone.\e[0m" echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m" echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' $SCRIPT_DIR/mailcow.conf
sleep 2 sleep 2
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://mailcow.github.io/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/mailcow-dockerized-docs/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://mailcow.github.io/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/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
exit 1 exit 1
fi fi
elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then
COMPOSE_COMMAND="docker compose" COMPOSE_COMMAND="docker compose"
# Check if Native Compose works and has not been deleted
if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
# IF it not exists/work anymore try the other command
COMPOSE_COMMAND="docker-compose"
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
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"
exit 1
fi
# If it finds the standalone Plugin it will use this instead and change the mailcow.conf Variable accordingly
echo -e "\e[31mFound different Docker Compose Version then declared in mailcow.conf!\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable from native to standalone\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' $SCRIPT_DIR/mailcow.conf
sleep 2
fi
elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
COMPOSE_COMMAND="docker-compose" COMPOSE_COMMAND="docker-compose"
# Check if Standalone Compose works and has not been deleted
if ! $COMPOSE_COMMAND > /dev/null 2>&1 && ! $COMPOSE_COMMAND --version > /dev/null 2>&1 | grep "^2." > /dev/null 2>&1; then
# IF it not exists/work anymore try the other command
COMPOSE_COMMAND="docker compose"
if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
# IF it cannot find Native in > 2.X, then script stops
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"
exit 1
fi
# If it finds the native Plugin it will use this instead and change the mailcow.conf Variable accordingly
echo -e "\e[31mFound different Docker Compose Version then declared in mailcow.conf!\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable from standalone to native\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' $SCRIPT_DIR/mailcow.conf
sleep 2
fi
fi fi
} }
@@ -326,8 +361,12 @@ while (($#)); do
echo -e "\e[32mRunning in forced mode...\e[0m" echo -e "\e[32mRunning in forced mode...\e[0m"
FORCE=y FORCE=y
;; ;;
-d|--dev)
echo -e "\e[32mRunning in Developer mode...\e[0m"
DEV=y
;;
--help|-h) --help|-h)
echo './update.sh [-c|--check, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -h|--help] echo './update.sh [-c|--check, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -d|--dev, -h|--help]
-c|--check - Check for updates and exit (exit codes => 0: update available, 3: no updates) -c|--check - Check for updates and exit (exit codes => 0: update available, 3: no updates)
--ours - Use merge strategy option "ours" to solve conflicts in favor of non-mailcow code (local changes over remote changes), not recommended! --ours - Use merge strategy option "ours" to solve conflicts in favor of non-mailcow code (local changes over remote changes), not recommended!
@@ -338,6 +377,7 @@ while (($#)); do
--skip-ping-check - Skip ICMP Check to public DNS resolvers (Use it only if you´ve blocked any ICMP Connections to your mailcow machine) --skip-ping-check - Skip ICMP Check to public DNS resolvers (Use it only if you´ve blocked any ICMP Connections to your mailcow machine)
--stable - Switch your mailcow updates to the stable (master) branch. Default unless you changed it with --nightly. --stable - Switch your mailcow updates to the stable (master) branch. Default unless you changed it with --nightly.
-f|--force - Force update, do not ask questions -f|--force - Force update, do not ask questions
-d|--dev - Enables Developer Mode (No Checkout of update.sh for tests)
' '
exit 1 exit 1
esac esac
@@ -388,8 +428,8 @@ CONFIG_ARRAY=(
"MAILDIR_GC_TIME" "MAILDIR_GC_TIME"
"MAILDIR_SUB" "MAILDIR_SUB"
"ACL_ANYONE" "ACL_ANYONE"
"XAPIAN_HEAP" "SOLR_HEAP"
"SKIP_XAPIAN" "SKIP_SOLR"
"ENABLE_SSL_SNI" "ENABLE_SSL_SNI"
"ALLOW_ADMIN_EMAIL_LOGIN" "ALLOW_ADMIN_EMAIL_LOGIN"
"SKIP_HTTP_VERIFICATION" "SKIP_HTTP_VERIFICATION"
@@ -510,20 +550,20 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# Otherwise a user might share data with too many other users.' >> mailcow.conf echo '# Otherwise a user might share data with too many other users.' >> mailcow.conf
echo 'ACL_ANYONE=disallow' >> mailcow.conf echo 'ACL_ANYONE=disallow' >> mailcow.conf
fi fi
elif [[ ${option} == "XAPIAN_HEAP" ]]; then elif [[ ${option} == "SOLR_HEAP" ]]; then
if ! grep -q ${option} mailcow.conf; then if ! grep -q ${option} mailcow.conf; then
echo "Replacing SOLR_HEAP with \"${option}\" in mailcow.conf" echo "Adding new option \"${option}\" to mailcow.conf"
sed -i '/# Solr heap size in MB, there is no recommendation, please see Solr docs./c\# Xapian heap size in MB, there is no recommendation, please see Xapians docs.' mailcow.conf echo '# Solr heap size, there is no recommendation, please see Solr docs.' >> mailcow.conf
sed -i '/# Solr is a prone to run OOM on large systems and should be monitored. Unmonitored Solr setups are not recommended./c\# Xapian is replacing solr completely. It is supposed to be much more efficient in CPU and RAM consumption.' mailcow.conf echo '# Solr is a prone to run OOM on large systems and should be monitored. Unmonitored Solr setups are not recommended.' >> mailcow.conf
sed -i '/SOLR_HEAP=/c\XAPIAN_HEAP=' mailcow.conf echo '# Solr will refuse to start with total system memory below or equal to 2 GB.' >> mailcow.conf
echo "SOLR_HEAP=1024" >> mailcow.conf
fi fi
elif [[ ${option} == "SKIP_XAPIAN" ]]; then elif [[ ${option} == "SKIP_SOLR" ]]; then
if ! grep -q ${option} mailcow.conf; then if ! grep -q ${option} mailcow.conf; then
echo "Replacing SKIP_SOLR with \"${option}\" in mailcow.conf" echo "Adding new option \"${option}\" to mailcow.conf"
sed -i '/# Skip Solr on low-memory systems or if you do not want to store a readable index of your mails in solr-vol-1./c\# Skip Xapian (FTS) on low-memory systems or if you do not want to store a readable index of your mails in vmail-index-vol-1.' mailcow.conf echo '# Solr is disabled by default after upgrading from non-Solr to Solr-enabled mailcows.' >> mailcow.conf
sed -i '/SKIP_SOLR=/c\SKIP_XAPIAN=' mailcow.conf echo '# Disable Solr or if you do not want to store a readable index of your mails in solr-vol-1.' >> mailcow.conf
echo "Removing Solr-Port Binding from mailcow.conf" echo "SKIP_SOLR=y" >> mailcow.conf
sed -i '/SOLR_PORT=/d' mailcow.conf
fi fi
elif [[ ${option} == "ENABLE_SSL_SNI" ]]; then elif [[ ${option} == "ENABLE_SSL_SNI" ]]; then
if ! grep -q ${option} mailcow.conf; then if ! grep -q ${option} mailcow.conf; then
@@ -597,7 +637,7 @@ for option in ${CONFIG_ARRAY[@]}; do
echo "Adding new option \"${option}\" to mailcow.conf" echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Password hash algorithm' >> mailcow.conf echo '# Password hash algorithm' >> mailcow.conf
echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf
echo '# see https://mailcow.github.io/mailcow-dockerized-docs/models/model-passwd/' >> mailcow.conf echo '# see https://docs.mailcow.email/models/model-passwd/' >> mailcow.conf
echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
fi fi
elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then
@@ -617,7 +657,7 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# Optional: Leave empty for none' >> mailcow.conf echo '# Optional: Leave empty for none' >> mailcow.conf
echo '# This value is only used on first order!' >> mailcow.conf echo '# This value is only used on first order!' >> mailcow.conf
echo '# Setting it at a later point will require the following steps:' >> mailcow.conf echo '# Setting it at a later point will require the following steps:' >> mailcow.conf
echo '# https://mailcow.github.io/mailcow-dockerized-docs/troubleshooting/debug-reset_tls/' >> mailcow.conf echo '# https://docs.mailcow.email/troubleshooting/debug-reset_tls/' >> mailcow.conf
echo 'ACME_CONTACT=' >> mailcow.conf echo 'ACME_CONTACT=' >> mailcow.conf
fi fi
elif [[ ${option} == "WEBAUTHN_ONLY_TRUSTED_VENDORS" ]]; then elif [[ ${option} == "WEBAUTHN_ONLY_TRUSTED_VENDORS" ]]; then
@@ -727,15 +767,17 @@ elif [ $NEW_BRANCH == "nightly" ] && [ $CURRENT_BRANCH != "nightly" ]; then
git checkout -f ${BRANCH} git checkout -f ${BRANCH}
fi fi
echo -e "\e[32mChecking for newer update script...\e[0m" if [ ! $DEV ]; then
SHA1_1=$(sha1sum update.sh) echo -e "\e[32mChecking for newer update script...\e[0m"
git fetch origin #${BRANCH} SHA1_1=$(sha1sum update.sh)
git checkout origin/${BRANCH} update.sh git fetch origin #${BRANCH}
SHA1_2=$(sha1sum update.sh) git checkout origin/${BRANCH} update.sh
if [[ ${SHA1_1} != ${SHA1_2} ]]; then SHA1_2=$(sha1sum update.sh)
echo "update.sh changed, please run this script again, exiting." if [[ ${SHA1_1} != ${SHA1_2} ]]; then
chmod +x update.sh echo "update.sh changed, please run this script again, exiting."
exit 2 chmod +x update.sh
exit 2
fi
fi fi
if [ ! $FORCE ]; then if [ ! $FORCE ]; then
@@ -902,9 +944,6 @@ else
echo -e "\e[33mCannot determine current git repository version...\e[0m" echo -e "\e[33mCannot determine current git repository version...\e[0m"
fi fi
# Set DOCKER_COMPOSE_VERSION
sed -i 's/^DOCKER_COMPOSE_VERSION=$/DOCKER_COMPOSE_VERSION='$DOCKER_COMPOSE_VERSION'/g' mailcow.conf
if [[ ${SKIP_START} == "y" ]]; then if [[ ${SKIP_START} == "y" ]]; then
echo -e "\e[33mNot starting mailcow, please run \"$COMPOSE_COMMAND up -d --remove-orphans\" to start mailcow.\e[0m" echo -e "\e[33mNot starting mailcow, please run \"$COMPOSE_COMMAND up -d --remove-orphans\" to start mailcow.\e[0m"
else else