mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-04-13 00:58:50 +00:00
Compare commits
295 Commits
2025-03a
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d371293e0 | ||
|
|
215a8addff | ||
|
|
8f58ba8bc8 | ||
|
|
70affa0f69 | ||
|
|
4ef65fc382 | ||
|
|
dbb9e474b0 | ||
|
|
f8eed8c786 | ||
|
|
ef010aa39c | ||
|
|
79171ea6f5 | ||
|
|
4e3294b273 | ||
|
|
32a6ecddb6 | ||
|
|
f3d9833ecf | ||
|
|
930ca76ea7 | ||
|
|
9a2887cf46 | ||
|
|
9950914086 | ||
|
|
470cfb0026 | ||
|
|
6c106b4e4d | ||
|
|
3d6253a2b2 | ||
|
|
b873812588 | ||
|
|
514fefd2ed | ||
|
|
6f9ee2d151 | ||
|
|
9832006141 | ||
|
|
0413d26855 | ||
|
|
7b29c1f304 | ||
|
|
ae3ef391ee | ||
|
|
7313f996d3 | ||
|
|
62d16c9e56 | ||
|
|
674b41ce08 | ||
|
|
1b833be760 | ||
|
|
88adb1adf5 | ||
|
|
ec472f13cf | ||
|
|
2e1d98cc7c | ||
|
|
07d7e3dc30 | ||
|
|
b0f5aee628 | ||
|
|
d3065612fd | ||
|
|
9912e41f78 | ||
|
|
04200c99a4 | ||
|
|
45666d2c4e | ||
|
|
9a806e64ce | ||
|
|
22a09b9795 | ||
|
|
04d5c43550 | ||
|
|
fbcb8cbeb9 | ||
|
|
0338a36ecf | ||
|
|
23fb5e2fca | ||
|
|
3507ff2773 | ||
|
|
a4970397f1 | ||
|
|
4132f6bd48 | ||
|
|
586b3a2ed1 | ||
|
|
6af2addf3c | ||
|
|
f6eed6c441 | ||
|
|
b85837c803 | ||
|
|
653fc40d4c | ||
|
|
c17d80a6fd | ||
|
|
980bfa3aa0 | ||
|
|
664a954393 | ||
|
|
d5a27c4ccb | ||
|
|
6a8a2e2136 | ||
|
|
b859a52b8e | ||
|
|
10e0c42eff | ||
|
|
f47df263d7 | ||
|
|
2642d9109e | ||
|
|
6708b94ebb | ||
|
|
79cf0abc6e | ||
|
|
7de70322d6 | ||
|
|
417835dea8 | ||
|
|
3dcacc4187 | ||
|
|
69f0552d4f | ||
|
|
c443a9400a | ||
|
|
5c9f387d94 | ||
|
|
e9414d17e4 | ||
|
|
6bfa58611e | ||
|
|
df4d3bb6e0 | ||
|
|
e31b6d9a07 | ||
|
|
455ef084b4 | ||
|
|
c2948735f2 | ||
|
|
24c62b2f09 | ||
|
|
1ef0149076 | ||
|
|
922d173540 | ||
|
|
fd088cb504 | ||
|
|
721ee2394e | ||
|
|
c217be06c6 | ||
|
|
871c422ec1 | ||
|
|
3cc28af607 | ||
|
|
796e131c3a | ||
|
|
dd160cd508 | ||
|
|
732b321962 | ||
|
|
c51a769aec | ||
|
|
45a61755a5 | ||
|
|
769c57c355 | ||
|
|
2e7eb7c0fd | ||
|
|
4c83147d01 | ||
|
|
ca0bec4fc2 | ||
|
|
6f50dd17da | ||
|
|
4a331929d0 | ||
|
|
748bc893b6 | ||
|
|
e462602ddc | ||
|
|
4e0f435d12 | ||
|
|
46f0581936 | ||
|
|
20f04ecf6b | ||
|
|
ff43799763 | ||
|
|
85ca197615 | ||
|
|
d06d23bbaf | ||
|
|
702ed85dfd | ||
|
|
8abe74a562 | ||
|
|
2f8a181281 | ||
|
|
5c5287ca21 | ||
|
|
83ba8d5840 | ||
|
|
ce219668cf | ||
|
|
5b1b49a418 | ||
|
|
8978a9ad79 | ||
|
|
5f4a4fd759 | ||
|
|
171c591da4 | ||
|
|
9133b9899c | ||
|
|
701c9fb1b4 | ||
|
|
eabd22188b | ||
|
|
7028619742 | ||
|
|
c915bf2ee2 | ||
|
|
011edd5ac9 | ||
|
|
7ba3de4ced | ||
|
|
8ead77083f | ||
|
|
b2774fb50b | ||
|
|
4440bd46ad | ||
|
|
28985973eb | ||
|
|
f2c4697ca3 | ||
|
|
383b5affb5 | ||
|
|
ed4dcff63b | ||
|
|
caca32bbba | ||
|
|
d31e74c778 | ||
|
|
6c00e29276 | ||
|
|
9940c503a2 | ||
|
|
4b2862cb3c | ||
|
|
a36485f0f1 | ||
|
|
78168ee80a | ||
|
|
610609378f | ||
|
|
260906e350 | ||
|
|
2891bbf82a | ||
|
|
eb26bcbc94 | ||
|
|
ef0f366d1c | ||
|
|
84e230de8f | ||
|
|
f67a12d157 | ||
|
|
34b48eedfc | ||
|
|
0d900d4fc8 | ||
|
|
642ac6d02c | ||
|
|
4db1569c93 | ||
|
|
94c1a6c4e1 | ||
|
|
7ce3b0faed | ||
|
|
262fe04286 | ||
|
|
b1c088a57f | ||
|
|
1c438330c6 | ||
|
|
8cb25709ae | ||
|
|
221f2989b0 | ||
|
|
3d05207bc7 | ||
|
|
8c8497d885 | ||
|
|
56d083ced4 | ||
|
|
a90b3544a7 | ||
|
|
08aea7fb26 | ||
|
|
13f7f9830b | ||
|
|
2f75039194 | ||
|
|
1e192e14f4 | ||
|
|
9cd1f931fc | ||
|
|
8d7235b535 | ||
|
|
8446abd484 | ||
|
|
f67c0530f5 | ||
|
|
06db1d6a72 | ||
|
|
81775ab4d5 | ||
|
|
34877ecf9c | ||
|
|
dbde144014 | ||
|
|
5361a4a4ee | ||
|
|
0997548d7f | ||
|
|
921de02a2b | ||
|
|
48e90a72dc | ||
|
|
c0b7a98e6c | ||
|
|
6dc90186f9 | ||
|
|
0b0a65a3f3 | ||
|
|
6c5d82c4df | ||
|
|
5e66ffa366 | ||
|
|
4d88e19106 | ||
|
|
29e28b47ed | ||
|
|
1cb38bacdb | ||
|
|
169aafec50 | ||
|
|
3826c4b5be | ||
|
|
e1410baaeb | ||
|
|
c39712af67 | ||
|
|
53c35493a5 | ||
|
|
af871fdacb | ||
|
|
2b93b59cdd | ||
|
|
2b2da1679e | ||
|
|
8cdb0b869e | ||
|
|
1e42b8dd21 | ||
|
|
842cb235b6 | ||
|
|
e91d678bd1 | ||
|
|
ef5739c32f | ||
|
|
88bf9b02e1 | ||
|
|
3803b5d351 | ||
|
|
14d58c8163 | ||
|
|
728fcdb375 | ||
|
|
1fc36263dc | ||
|
|
69420113f7 | ||
|
|
360fe03497 | ||
|
|
7557802933 | ||
|
|
2e9ba1e9b3 | ||
|
|
795bcdc5d2 | ||
|
|
ad9b328ed5 | ||
|
|
3d5b57889a | ||
|
|
6b8e981bdc | ||
|
|
2f1eb4b004 | ||
|
|
3ee3d7d969 | ||
|
|
95eb350f15 | ||
|
|
1e5fcfe392 | ||
|
|
527f27d249 | ||
|
|
02557b2098 | ||
|
|
4c7a9ed195 | ||
|
|
d5b30a7a08 | ||
|
|
b7acef4d9d | ||
|
|
fc43c26c48 | ||
|
|
b12ce1eacd | ||
|
|
ec6dbb099a | ||
|
|
2fbbbbe9a9 | ||
|
|
1e4f3c55d8 | ||
|
|
a0f5454c2a | ||
|
|
4e7adacda9 | ||
|
|
4c64cf18a6 | ||
|
|
8a89f5c685 | ||
|
|
cc0e4fee9d | ||
|
|
5861c9af29 | ||
|
|
dd475c0ab3 | ||
|
|
407e9d3584 | ||
|
|
d4f899b091 | ||
|
|
372923ae2f | ||
|
|
3bd01190bf | ||
|
|
1994b9895b | ||
|
|
03d979c089 | ||
|
|
798e6a4c00 | ||
|
|
ffa2933873 | ||
|
|
7f47a3f00e | ||
|
|
1bcab9a9a5 | ||
|
|
1b2f424edc | ||
|
|
486b297409 | ||
|
|
75d7f06b25 | ||
|
|
ea0944d743 | ||
|
|
cb6ffe65c8 | ||
|
|
580dabd276 | ||
|
|
846862aa80 | ||
|
|
e7a1f24c78 | ||
|
|
8ff0e029f0 | ||
|
|
0680b21938 | ||
|
|
0c8e7bfeca | ||
|
|
badcd27b93 | ||
|
|
7d3ef3d67f | ||
|
|
5b89e253a6 | ||
|
|
a90f4c2a2e | ||
|
|
db7b917944 | ||
|
|
401b744808 | ||
|
|
0c83255573 | ||
|
|
d55f0fc366 | ||
|
|
06b3ba91a0 | ||
|
|
aa4125fe62 | ||
|
|
d8c6ed9191 | ||
|
|
cb47fa406f | ||
|
|
c4d0f35008 | ||
|
|
0d3e8dd738 | ||
|
|
692355a08a | ||
|
|
a370499aaa | ||
|
|
84f67d6608 | ||
|
|
4ac839cf49 | ||
|
|
b96a5b1efd | ||
|
|
766c5e8580 | ||
|
|
3f493e043d | ||
|
|
3ddad9dee8 | ||
|
|
2c10c39bc4 | ||
|
|
0eb8f38792 | ||
|
|
402bf53a5c | ||
|
|
428a59dd3f | ||
|
|
153890b283 | ||
|
|
a741c2ba4a | ||
|
|
741e5c719f | ||
|
|
34e4f93db9 | ||
|
|
3758135dc3 | ||
|
|
6794e6ff43 | ||
|
|
62f816e64a | ||
|
|
e65478076b | ||
|
|
ceeabded73 | ||
|
|
805634f9a9 | ||
|
|
a92832d115 | ||
|
|
4c5f485587 | ||
|
|
db3a577ae3 | ||
|
|
e452917de9 | ||
|
|
f37961b7d0 | ||
|
|
0157cbddaf | ||
|
|
65d872cc14 | ||
|
|
4ad2422810 | ||
|
|
8408b82e9c | ||
|
|
cd3b1ab828 | ||
|
|
ceebc56e62 | ||
|
|
70190e5230 |
69
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
69
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
@@ -11,22 +11,35 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: I've found a bug and checked that ...
|
label: Checklist prior issue creation
|
||||||
description: Prior to placing the issue, please check following:** *(fill out each checkbox with an `X` once done)*
|
description: Prior to creating the issue...
|
||||||
options:
|
options:
|
||||||
- label: ... I understand that not following the below instructions will result in immediate closure and/or deletion of my issue.
|
- label: I understand that failure to follow below instructions may cause this issue to be closed.
|
||||||
required: true
|
required: true
|
||||||
- label: ... I have understood that this bug report is dedicated for bugs, and not for support-related inquiries.
|
- label: I understand that vague, incomplete or inaccurate information may cause this issue to be closed.
|
||||||
required: true
|
required: true
|
||||||
- label: ... I have understood that answers are voluntary and community-driven, and not commercial support.
|
- label: I understand that this form is intended solely for reporting software bugs and not for support-related inquiries.
|
||||||
required: true
|
required: true
|
||||||
- label: ... I have verified that my issue has not been already answered in the past. I also checked previous [issues](https://github.com/mailcow/mailcow-dockerized/issues).
|
- label: I understand that all responses are voluntary and community-driven, and do not constitute commercial support.
|
||||||
|
required: true
|
||||||
|
- label: I confirm that I have reviewed previous [issues](https://github.com/mailcow/mailcow-dockerized/issues) to ensure this matter has not already been addressed.
|
||||||
|
required: true
|
||||||
|
- label: I confirm that my environment meets all [prerequisite requirements](https://docs.mailcow.email/getstarted/prerequisite-system/) as specified in the official documentation.
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
description: Please provide a brief description of the bug in 1-2 sentences. If applicable, add screenshots to help explain your problem. Very useful for bugs in mailcow UI.
|
description: Please provide a brief description of the bug. If applicable, add screenshots to help explain your problem. (Very useful for bugs in mailcow UI.)
|
||||||
render: plain text
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: "Steps to reproduce:"
|
||||||
|
description: "Please describe the steps to reproduce the bug. Screenshots can be added, if helpful."
|
||||||
|
placeholder: |-
|
||||||
|
1. ...
|
||||||
|
2. ...
|
||||||
|
3. ...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@@ -36,45 +49,36 @@ body:
|
|||||||
render: plain text
|
render: plain text
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: "Steps to reproduce:"
|
|
||||||
description: "Please describe the steps to reproduce the bug. Screenshots can be added, if helpful."
|
|
||||||
render: plain text
|
|
||||||
placeholder: |-
|
|
||||||
1. ...
|
|
||||||
2. ...
|
|
||||||
3. ...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
## System information
|
## System information
|
||||||
### In this stage we would kindly ask you to attach general system information about your setup.
|
In this stage we would kindly ask you to attach general system information about your setup.
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
attributes:
|
attributes:
|
||||||
label: "Which branch are you using?"
|
label: "Which branch are you using?"
|
||||||
description: "#### `git rev-parse --abbrev-ref HEAD`"
|
description: "#### Run: `git rev-parse --abbrev-ref HEAD`"
|
||||||
multiple: false
|
multiple: false
|
||||||
options:
|
options:
|
||||||
- master
|
- master (stable)
|
||||||
|
- staging
|
||||||
- nightly
|
- nightly
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
attributes:
|
attributes:
|
||||||
label: "Which architecture are you using?"
|
label: "Which architecture are you using?"
|
||||||
description: "#### `uname -m`"
|
description: "#### Run: `uname -m`"
|
||||||
multiple: false
|
multiple: false
|
||||||
options:
|
options:
|
||||||
- x86
|
- x86_64
|
||||||
- ARM64 (aarch64)
|
- ARM64 (aarch64)
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: "Operating System:"
|
label: "Operating System:"
|
||||||
|
description: "#### Run: `lsb_release -ds`"
|
||||||
placeholder: "e.g. Ubuntu 22.04 LTS"
|
placeholder: "e.g. Ubuntu 22.04 LTS"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@@ -93,43 +97,44 @@ body:
|
|||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: "Virtualization technology:"
|
label: "Virtualization technology:"
|
||||||
placeholder: "KVM, VMware, Xen, etc - **LXC and OpenVZ are not supported**"
|
description: "LXC and OpenVZ are not supported!"
|
||||||
|
placeholder: "KVM, VMware ESXi, Xen, etc"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: "Docker version:"
|
label: "Docker version:"
|
||||||
description: "#### `docker version`"
|
description: "#### Run: `docker version`"
|
||||||
placeholder: "20.10.21"
|
placeholder: "20.10.21"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: "docker-compose version or docker compose version:"
|
label: "docker-compose version or docker compose version:"
|
||||||
description: "#### `docker-compose version` or `docker compose version`"
|
description: "#### Run: `docker-compose version` or `docker compose version`"
|
||||||
placeholder: "v2.12.2"
|
placeholder: "v2.12.2"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: "mailcow version:"
|
label: "mailcow version:"
|
||||||
description: "#### ```git describe --tags `git rev-list --tags --max-count=1` ```"
|
description: "#### Run: ```git describe --tags `git rev-list --tags --max-count=1` ```"
|
||||||
placeholder: "2022-08"
|
placeholder: "2022-08x"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: "Reverse proxy:"
|
label: "Reverse proxy:"
|
||||||
placeholder: "e.g. Nginx/Traefik"
|
placeholder: "e.g. nginx/Traefik, or none"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "Logs of git diff:"
|
label: "Logs of git diff:"
|
||||||
description: "#### Output of `git diff origin/master`, any other changes to the code? If so, **please post them**:"
|
description: "#### Output of `git diff origin/master`, any other changes to the code? Sanitize if needed. If so, **please post them**:"
|
||||||
render: plain text
|
render: plain text
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "Logs of iptables -L -vn:"
|
label: "Logs of iptables -L -vn:"
|
||||||
|
|||||||
@@ -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@v9.1.0
|
uses: actions/stale@v10.1.1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
||||||
days-before-stale: 60
|
days-before-stale: 60
|
||||||
|
|||||||
2
.github/workflows/image_builds.yml
vendored
2
.github/workflows/image_builds.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
|||||||
- "watchdog-mailcow"
|
- "watchdog-mailcow"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Setup Docker
|
- name: Setup Docker
|
||||||
run: |
|
run: |
|
||||||
curl -sSL https://get.docker.com/ | CHANNEL=stable sudo sh
|
curl -sSL https://get.docker.com/ | CHANNEL=stable sudo sh
|
||||||
|
|||||||
4
.github/workflows/pr_to_nightly.yml
vendored
4
.github/workflows/pr_to_nightly.yml
vendored
@@ -8,11 +8,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Run the Action
|
- name: Run the Action
|
||||||
uses: devops-infra/action-pull-request@v0.6.0
|
uses: devops-infra/action-pull-request@v1.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
||||||
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
||||||
|
|||||||
2
.github/workflows/rebuild_backup_image.yml
vendored
2
.github/workflows/rebuild_backup_image.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Generate postscreen_access.cidr
|
- name: Generate postscreen_access.cidr
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -75,3 +75,4 @@ refresh_images.sh
|
|||||||
update_diffs/
|
update_diffs/
|
||||||
create_cold_standby.sh
|
create_cold_standby.sh
|
||||||
!data/conf/nginx/mailcow_auth.conf
|
!data/conf/nginx/mailcow_auth.conf
|
||||||
|
data/conf/postfix/postfix-tlspol
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
# Contribution Guidelines
|
# Contribution Guidelines
|
||||||
**_Last modified on 15th August 2024_**
|
**_Last modified on 12th November 2025_**
|
||||||
|
|
||||||
First of all, thank you for wanting to provide a bugfix or a new feature for the mailcow community, it's because of your help that the project can continue to grow!
|
First of all, thank you for wanting to provide a bugfix or a new feature for the mailcow community, it's because of your help that the project can continue to grow!
|
||||||
|
|
||||||
As we want to keep mailcow's development structured we setup these Guidelines which helps you to create your issue/pull request accordingly.
|
As we want to keep mailcow's development structured we setup these Guidelines which helps you to create your issue/pull request accordingly.
|
||||||
|
|
||||||
**PLEASE NOTE, THAT WE MIGHT CLOSE ISSUES/PULL REQUESTS IF THEY DON'T FULLFIL OUR WRITTEN GUIDELINES WRITTEN INSIDE THIS DOCUMENT**. So please check this guidelines before you propose a Issue/Pull Request.
|
**PLEASE NOTE, THAT WE WILL CLOSE ISSUES/PULL REQUESTS IF THEY DON'T FULFILL OUR WRITTEN GUIDELINES WRITTEN INSIDE THIS DOCUMENT**. So please check this guidelines before you propose a Issue/Pull Request.
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
|
||||||
@@ -27,14 +27,18 @@ However, please note the following regarding pull requests:
|
|||||||
6. Please **ALWAYS** create the actual pull request against the staging branch and **NEVER** directly against the master branch. *If you forget to do this, our moobot will remind you to switch the branch to staging.*
|
6. Please **ALWAYS** create the actual pull request against the staging branch and **NEVER** directly against the master branch. *If you forget to do this, our moobot will remind you to switch the branch to staging.*
|
||||||
7. Wait for a merge commit: It may happen that we do not accept your pull request immediately or sometimes not at all for various reasons. Please do not be disappointed if this is the case. We always endeavor to incorporate any meaningful changes from the community into the mailcow project.
|
7. Wait for a merge commit: It may happen that we do not accept your pull request immediately or sometimes not at all for various reasons. Please do not be disappointed if this is the case. We always endeavor to incorporate any meaningful changes from the community into the mailcow project.
|
||||||
8. If you are planning larger and therefore more complex pull requests, it would be advisable to first announce this in a separate issue and then start implementing it after the idea has been accepted in order to avoid unnecessary frustration and effort!
|
8. If you are planning larger and therefore more complex pull requests, it would be advisable to first announce this in a separate issue and then start implementing it after the idea has been accepted in order to avoid unnecessary frustration and effort!
|
||||||
|
9. If your PR requires a Docker image rebuild (changes to Dockerfiles or files in data/Dockerfiles/), update the image tag in docker-compose.yml. Use the base-image versioning (e.g. ghcr.io/mailcow/sogo:5.12.4 → :5.12.5 for version bumps; append a letter for patch fixes, e.g. :5.12.4a). Follow this scheme.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Issue Reporting
|
## Issue Reporting
|
||||||
**_Last modified on 15th August 2024_**
|
**_Last modified on 12th November 2025_**
|
||||||
|
|
||||||
If you plan to report a issue within mailcow please read and understand the following rules:
|
If you plan to report a issue within mailcow please read and understand the following rules:
|
||||||
|
|
||||||
|
### Security disclosures / Security-related fixes
|
||||||
|
- Security vulnerabilities and security fixes must always be reported confidentially first to the contact address specified in SECURITY.md before they are integrated, published, or publicly disclosed in issues/PRs. Please wait for a response from the specified contact to ensure coordinated and responsible disclosure.
|
||||||
|
|
||||||
### Issue Reporting Guidelines
|
### Issue Reporting Guidelines
|
||||||
|
|
||||||
1. **ONLY** use the issue tracker for bug reports or improvement requests and NOT for support questions. For support questions you can either contact the [mailcow community on Telegram](https://docs.mailcow.email/#community-support-and-chat) or the mailcow team directly in exchange for a [support fee](https://docs.mailcow.email/#commercial-support).
|
1. **ONLY** use the issue tracker for bug reports or improvement requests and NOT for support questions. For support questions you can either contact the [mailcow community on Telegram](https://docs.mailcow.email/#community-support-and-chat) or the mailcow team directly in exchange for a [support fee](https://docs.mailcow.email/#commercial-support).
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -13,6 +13,22 @@ You can also [get a SAL](https://www.servercow.de/mailcow?lang=en#sal) which is
|
|||||||
|
|
||||||
Or just spread the word: moo.
|
Or just spread the word: moo.
|
||||||
|
|
||||||
|
## Many thanks to our GitHub Sponsors ❤️
|
||||||
|
A big thank you to everyone supporting us on GitHub Sponsors—your contributions mean the world to us! Special thanks to the following amazing supporters:
|
||||||
|
|
||||||
|
### 100$/Month Sponsors
|
||||||
|
<a href="https://www.colba.net/" target=_blank><img
|
||||||
|
src="https://avatars.githubusercontent.com/u/204464723" height="58"
|
||||||
|
/></a>
|
||||||
|
<a href="https://www.maehdros.com/" target=_blank><img
|
||||||
|
src="https://avatars.githubusercontent.com/u/173894712" height="58"
|
||||||
|
/></a>
|
||||||
|
|
||||||
|
### 50$/Month Sponsors
|
||||||
|
<a href="https://github.com/vnukhr" target=_blank><img
|
||||||
|
src="https://avatars.githubusercontent.com/u/7805987?s=52&v=4" height="58"
|
||||||
|
/></a>
|
||||||
|
|
||||||
## Info, documentation and support
|
## Info, documentation and support
|
||||||
|
|
||||||
Please see [the official documentation](https://docs.mailcow.email/) for installation and support instructions. 🐄
|
Please see [the official documentation](https://docs.mailcow.email/) for installation and support instructions. 🐄
|
||||||
|
|||||||
230
_modules/scripts/core.sh
Normal file
230
_modules/scripts/core.sh
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# _modules/scripts/core.sh
|
||||||
|
# THIS SCRIPT IS DESIGNED TO BE RUNNING BY MAILCOW SCRIPTS ONLY!
|
||||||
|
# DO NOT, AGAIN, NOT TRY TO RUN THIS SCRIPT STANDALONE!!!!!!
|
||||||
|
|
||||||
|
# ANSI color for red errors
|
||||||
|
RED='\e[31m'
|
||||||
|
GREEN='\e[32m'
|
||||||
|
YELLOW='\e[33m'
|
||||||
|
BLUE='\e[34m'
|
||||||
|
MAGENTA='\e[35m'
|
||||||
|
LIGHT_RED='\e[91m'
|
||||||
|
LIGHT_GREEN='\e[92m'
|
||||||
|
NC='\e[0m'
|
||||||
|
|
||||||
|
caller="${BASH_SOURCE[1]##*/}"
|
||||||
|
|
||||||
|
get_installed_tools(){
|
||||||
|
for bin in openssl curl docker git awk sha1sum grep cut jq; do
|
||||||
|
if [[ -z $(command -v ${bin}) ]]; then
|
||||||
|
echo "Error: Cannot find command '${bin}'. Cannot proceed."
|
||||||
|
echo "Solution: Please review system requirements and install requirements. Then, re-run the script."
|
||||||
|
echo "See System Requirements: https://docs.mailcow.email/getstarted/install/"
|
||||||
|
echo "Exiting..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if grep --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo -e "${LIGHT_RED}BusyBox grep detected, please install gnu grep, \"apk add --no-cache --upgrade grep\"${NC}"; exit 1; fi
|
||||||
|
# This will also cover sort
|
||||||
|
if cp --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo -e "${LIGHT_RED}BusyBox cp detected, please install coreutils, \"apk add --no-cache --upgrade coreutils\"${NC}"; exit 1; fi
|
||||||
|
if sed --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo -e "${LIGHT_RED}BusyBox sed detected, please install gnu sed, \"apk add --no-cache --upgrade sed\"${NC}"; exit 1; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_docker_version(){
|
||||||
|
# Check Docker Version (need at least 24.X)
|
||||||
|
docker_version=$(docker version --format '{{.Server.Version}}' | cut -d '.' -f 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
get_compose_type(){
|
||||||
|
if docker compose > /dev/null 2>&1; then
|
||||||
|
if docker compose version --short | grep -e "^[2-9]\." -e "^v[2-9]\." -e "^[1-9][0-9]\." -e "^v[1-9][0-9]\." > /dev/null 2>&1; then
|
||||||
|
COMPOSE_VERSION=native
|
||||||
|
COMPOSE_COMMAND="docker compose"
|
||||||
|
if [[ "$caller" == "update.sh" ]]; then
|
||||||
|
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' "$SCRIPT_DIR/mailcow.conf"
|
||||||
|
fi
|
||||||
|
echo -e "\e[33mFound Docker Compose Plugin (native).\e[0m"
|
||||||
|
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
|
||||||
|
sleep 2
|
||||||
|
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
|
||||||
|
else
|
||||||
|
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
||||||
|
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
elif docker-compose > /dev/null 2>&1; then
|
||||||
|
if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then
|
||||||
|
if docker-compose version --short | grep -e "^[2-9]\." -e "^[1-9][0-9]\." > /dev/null 2>&1; then
|
||||||
|
COMPOSE_VERSION=standalone
|
||||||
|
COMPOSE_COMMAND="docker-compose"
|
||||||
|
if [[ "$caller" == "update.sh" ]]; then
|
||||||
|
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' "$SCRIPT_DIR/mailcow.conf"
|
||||||
|
fi
|
||||||
|
echo -e "\e[33mFound Docker Compose Standalone.\e[0m"
|
||||||
|
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
|
||||||
|
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"
|
||||||
|
else
|
||||||
|
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
||||||
|
echo -e "\e[31mPlease update/install manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "\e[31mCannot find Docker Compose.\e[0m"
|
||||||
|
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_bad_asn() {
|
||||||
|
echo -e "\e[33mDetecting if your IP is listed on Spamhaus Bad ASN List...\e[0m"
|
||||||
|
response=$(curl --connect-timeout 15 --max-time 30 -s -o /dev/null -w "%{http_code}" "https://asn-check.mailcow.email")
|
||||||
|
if [ "$response" -eq 503 ]; then
|
||||||
|
if [ -z "$SPAMHAUS_DQS_KEY" ]; then
|
||||||
|
echo -e "\e[33mYour server's public IP uses an AS that is blocked by Spamhaus to use their DNS public blocklists for Postfix.\e[0m"
|
||||||
|
echo -e "\e[33mmailcow did not detected a value for the variable SPAMHAUS_DQS_KEY inside mailcow.conf!\e[0m"
|
||||||
|
sleep 2
|
||||||
|
echo ""
|
||||||
|
echo -e "\e[33mTo use the Spamhaus DNS Blocklists again, you will need to create a FREE account for their Data Query Service (DQS) at: https://www.spamhaus.com/free-trial/sign-up-for-a-free-data-query-service-account\e[0m"
|
||||||
|
echo -e "\e[33mOnce done, enter your DQS API key in mailcow.conf and mailcow will do the rest for you!\e[0m"
|
||||||
|
echo ""
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
echo -e "\e[33mYour server's public IP uses an AS that is blocked by Spamhaus to use their DNS public blocklists for Postfix.\e[0m"
|
||||||
|
echo -e "\e[32mmailcow detected a Value for the variable SPAMHAUS_DQS_KEY inside mailcow.conf. Postfix will use DQS with the given API key...\e[0m"
|
||||||
|
fi
|
||||||
|
elif [ "$response" -eq 200 ]; then
|
||||||
|
echo -e "\e[33mCheck completed! Your IP is \e[32mclean\e[0m"
|
||||||
|
elif [ "$response" -eq 429 ]; then
|
||||||
|
echo -e "\e[33mCheck completed! \e[31mYour IP seems to be rate limited on the ASN Check service... please try again later!\e[0m"
|
||||||
|
else
|
||||||
|
echo -e "\e[31mCheck failed! \e[0mMaybe a DNS or Network problem?\e[0m"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_online_status() {
|
||||||
|
CHECK_ONLINE_DOMAINS=('https://github.com' 'https://hub.docker.com')
|
||||||
|
for domain in "${CHECK_ONLINE_DOMAINS[@]}"; do
|
||||||
|
if timeout 6 curl --head --silent --output /dev/null ${domain}; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
prefetch_images() {
|
||||||
|
[[ -z ${BRANCH} ]] && { echo -e "\e[33m\nUnknown branch...\e[0m"; exit 1; }
|
||||||
|
git fetch origin #${BRANCH}
|
||||||
|
while read image; do
|
||||||
|
RET_C=0
|
||||||
|
until docker pull "${image}"; do
|
||||||
|
RET_C=$((RET_C + 1))
|
||||||
|
echo -e "\e[33m\nError pulling $image, retrying...\e[0m"
|
||||||
|
[ ${RET_C} -gt 3 ] && { echo -e "\e[31m\nToo many failed retries, exiting\e[0m"; exit 1; }
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
done < <(git show "origin/${BRANCH}:docker-compose.yml" | grep "image:" | awk '{ gsub("image:","", $3); print $2 }')
|
||||||
|
}
|
||||||
|
|
||||||
|
docker_garbage() {
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )"
|
||||||
|
IMGS_TO_DELETE=()
|
||||||
|
|
||||||
|
declare -A IMAGES_INFO
|
||||||
|
COMPOSE_IMAGES=($(grep -oP "image: \K(ghcr\.io/)?mailcow.+" "${SCRIPT_DIR}/docker-compose.yml"))
|
||||||
|
|
||||||
|
for existing_image in $(docker images --format "{{.ID}}:{{.Repository}}:{{.Tag}}" | grep -E '(mailcow/|ghcr\.io/mailcow/)'); do
|
||||||
|
ID=$(echo "$existing_image" | cut -d ':' -f 1)
|
||||||
|
REPOSITORY=$(echo "$existing_image" | cut -d ':' -f 2)
|
||||||
|
TAG=$(echo "$existing_image" | cut -d ':' -f 3)
|
||||||
|
|
||||||
|
if [[ "$REPOSITORY" == "mailcow/backup" || "$REPOSITORY" == "ghcr.io/mailcow/backup" ]]; then
|
||||||
|
if [[ "$TAG" != "<none>" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ " ${COMPOSE_IMAGES[@]} " =~ " ${REPOSITORY}:${TAG} " ]]; then
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
IMGS_TO_DELETE+=("$ID")
|
||||||
|
IMAGES_INFO["$ID"]="$REPOSITORY:$TAG"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then
|
||||||
|
echo "The following unused mailcow images were found:"
|
||||||
|
for id in "${IMGS_TO_DELETE[@]}"; do
|
||||||
|
echo " ${IMAGES_INFO[$id]} ($id)"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$FORCE" ]; then
|
||||||
|
read -r -p "Do you want to delete them to free up some space? [y/N] " response
|
||||||
|
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
|
docker rmi ${IMGS_TO_DELETE[*]}
|
||||||
|
else
|
||||||
|
echo "OK, skipped."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Running in forced mode! Force removing old mailcow images..."
|
||||||
|
docker rmi ${IMGS_TO_DELETE[*]}
|
||||||
|
fi
|
||||||
|
echo -e "\e[32mFurther cleanup...\e[0m"
|
||||||
|
echo "If you want to cleanup further garbage collected by Docker, please make sure all containers are up and running before cleaning your system by executing \"docker system prune\""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
in_array() {
|
||||||
|
local e match="$1"
|
||||||
|
shift
|
||||||
|
for e; do [[ "$e" == "$match" ]] && return 0; done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_major_update() {
|
||||||
|
if [ ${BRANCH} == "master" ]; then
|
||||||
|
# Array with major versions
|
||||||
|
# Add major versions here
|
||||||
|
MAJOR_VERSIONS=(
|
||||||
|
"2025-02"
|
||||||
|
"2025-03"
|
||||||
|
"2025-09"
|
||||||
|
)
|
||||||
|
|
||||||
|
current_version=""
|
||||||
|
if [[ -f "${SCRIPT_DIR}/data/web/inc/app_info.inc.php" ]]; then
|
||||||
|
current_version=$(grep 'MAILCOW_GIT_VERSION' ${SCRIPT_DIR}/data/web/inc/app_info.inc.php | sed -E 's/.*MAILCOW_GIT_VERSION="([^"]+)".*/\1/')
|
||||||
|
fi
|
||||||
|
if [[ -z "$current_version" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
release_url="https://github.com/mailcow/mailcow-dockerized/releases/tag"
|
||||||
|
|
||||||
|
updates_to_apply=()
|
||||||
|
|
||||||
|
for version in "${MAJOR_VERSIONS[@]}"; do
|
||||||
|
if [[ "$current_version" < "$version" ]]; then
|
||||||
|
updates_to_apply+=("$version")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#updates_to_apply[@]} -gt 0 ]]; then
|
||||||
|
echo -e "\e[33m\nMAJOR UPDATES to be applied:\e[0m"
|
||||||
|
for update in "${updates_to_apply[@]}"; do
|
||||||
|
echo "$update - $release_url/$update"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "\nPlease read the release notes before proceeding."
|
||||||
|
read -p "Do you want to proceed with the update? [y/n] " response
|
||||||
|
if [[ "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
|
echo "Proceeding with the update..."
|
||||||
|
else
|
||||||
|
echo "Update canceled. Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
272
_modules/scripts/ipv6_controller.sh
Normal file
272
_modules/scripts/ipv6_controller.sh
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# _modules/scripts/ipv6_controller.sh
|
||||||
|
# THIS SCRIPT IS DESIGNED TO BE RUNNING BY MAILCOW SCRIPTS ONLY!
|
||||||
|
# DO NOT, AGAIN, NOT TRY TO RUN THIS SCRIPT STANDALONE!!!!!!
|
||||||
|
|
||||||
|
# 1) Check if the host supports IPv6
|
||||||
|
get_ipv6_support() {
|
||||||
|
# ---- helper: probe external IPv6 connectivity without DNS ----
|
||||||
|
_probe_ipv6_connectivity() {
|
||||||
|
# Use literal, always-on IPv6 echo responders (no DNS required)
|
||||||
|
local PROBE_IPS=("2001:4860:4860::8888" "2606:4700:4700::1111")
|
||||||
|
local ip rc=1
|
||||||
|
|
||||||
|
for ip in "${PROBE_IPS[@]}"; do
|
||||||
|
if command -v ping6 &>/dev/null; then
|
||||||
|
ping6 -c1 -W2 "$ip" &>/dev/null || ping6 -c1 -w2 "$ip" &>/dev/null
|
||||||
|
rc=$?
|
||||||
|
elif command -v ping &>/dev/null; then
|
||||||
|
ping -6 -c1 -W2 "$ip" &>/dev/null || ping -6 -c1 -w2 "$ip" &>/dev/null
|
||||||
|
rc=$?
|
||||||
|
else
|
||||||
|
rc=1
|
||||||
|
fi
|
||||||
|
[[ $rc -eq 0 ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ! -f /proc/net/if_inet6 ]] || grep -qs '^1' /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null; then
|
||||||
|
DETECTED_IPV6=false
|
||||||
|
echo -e "${YELLOW}IPv6 not detected on host – ${LIGHT_RED}IPv6 is administratively disabled${YELLOW}.${NC}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ip -6 route show default 2>/dev/null | grep -qE '^default'; then
|
||||||
|
echo -e "${YELLOW}Default IPv6 route found – testing external IPv6 connectivity...${NC}"
|
||||||
|
if _probe_ipv6_connectivity; then
|
||||||
|
DETECTED_IPV6=true
|
||||||
|
echo -e "IPv6 detected on host – ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}"
|
||||||
|
else
|
||||||
|
DETECTED_IPV6=false
|
||||||
|
echo -e "${YELLOW}Default IPv6 route present but external IPv6 connectivity failed – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}"
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ip -6 addr show scope global 2>/dev/null | grep -q 'inet6'; then
|
||||||
|
DETECTED_IPV6=false
|
||||||
|
echo -e "${YELLOW}Global IPv6 address present but no default route – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ip -6 addr show scope link 2>/dev/null | grep -q 'inet6'; then
|
||||||
|
echo -e "${YELLOW}Only link-local IPv6 addresses found – testing external IPv6 connectivity...${NC}"
|
||||||
|
if _probe_ipv6_connectivity; then
|
||||||
|
DETECTED_IPV6=true
|
||||||
|
echo -e "External IPv6 connectivity available – ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}"
|
||||||
|
else
|
||||||
|
DETECTED_IPV6=false
|
||||||
|
echo -e "${YELLOW}Only link-local IPv6 present and no external connectivity – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}"
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
DETECTED_IPV6=false
|
||||||
|
echo -e "${YELLOW}IPv6 not detected on host – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2) Ensure Docker daemon.json has (or create) the required IPv6 settings
|
||||||
|
docker_daemon_edit(){
|
||||||
|
DOCKER_DAEMON_CONFIG="/etc/docker/daemon.json"
|
||||||
|
DOCKER_MAJOR=$(docker version --format '{{.Server.Version}}' 2>/dev/null | cut -d. -f1)
|
||||||
|
MISSING=()
|
||||||
|
|
||||||
|
_has_kv() { grep -Eq "\"$1\"[[:space:]]*:[[:space:]]*$2" "$DOCKER_DAEMON_CONFIG" 2>/dev/null; }
|
||||||
|
|
||||||
|
if [[ -f "$DOCKER_DAEMON_CONFIG" ]]; then
|
||||||
|
|
||||||
|
# reject empty or whitespace-only file immediately
|
||||||
|
if [[ ! -s "$DOCKER_DAEMON_CONFIG" ]] || ! grep -Eq '[{}]' "$DOCKER_DAEMON_CONFIG"; then
|
||||||
|
echo -e "${RED}ERROR: $DOCKER_DAEMON_CONFIG exists but is empty or contains no JSON braces – please initialize it with valid JSON (e.g. {}).${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate JSON if jq is present
|
||||||
|
if command -v jq &>/dev/null && ! jq empty "$DOCKER_DAEMON_CONFIG" &>/dev/null; then
|
||||||
|
echo -e "${RED}ERROR: Invalid JSON in $DOCKER_DAEMON_CONFIG – please correct manually.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gather missing keys
|
||||||
|
! _has_kv ipv6 true && MISSING+=("ipv6: true")
|
||||||
|
|
||||||
|
# For Docker < 28, keep requiring fixed-cidr-v6 (default bridge needs it on old engines)
|
||||||
|
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then
|
||||||
|
! grep -Eq '"fixed-cidr-v6"[[:space:]]*:[[:space:]]*".+"' "$DOCKER_DAEMON_CONFIG" \
|
||||||
|
&& MISSING+=('fixed-cidr-v6: "fd00:dead:beef:c0::/80"')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Docker < 27, ip6tables needed and was tied to experimental in older releases
|
||||||
|
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]]; then
|
||||||
|
_has_kv ipv6 true && ! _has_kv ip6tables true && MISSING+=("ip6tables: true")
|
||||||
|
! _has_kv experimental true && MISSING+=("experimental: true")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fix if needed
|
||||||
|
if ((${#MISSING[@]}>0)); then
|
||||||
|
echo -e "${MAGENTA}Your daemon.json is missing: ${YELLOW}${MISSING[*]}${NC}"
|
||||||
|
if [[ -n "$FORCE" ]]; then
|
||||||
|
ans=Y
|
||||||
|
else
|
||||||
|
read -p "Would you like to update $DOCKER_DAEMON_CONFIG now? [Y/n] " ans
|
||||||
|
ans=${ans:-Y}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ans =~ ^[Yy]$ ]]; then
|
||||||
|
cp "$DOCKER_DAEMON_CONFIG" "${DOCKER_DAEMON_CONFIG}.bak"
|
||||||
|
if command -v jq &>/dev/null; then
|
||||||
|
TMP=$(mktemp)
|
||||||
|
# Base filter: ensure ipv6 = true
|
||||||
|
JQ_FILTER='.ipv6 = true'
|
||||||
|
|
||||||
|
# Add fixed-cidr-v6 only for Docker < 28
|
||||||
|
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then
|
||||||
|
JQ_FILTER+=' | .["fixed-cidr-v6"] = (.["fixed-cidr-v6"] // "fd00:dead:beef:c0::/80")'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add ip6tables/experimental only for Docker < 27
|
||||||
|
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]]; then
|
||||||
|
JQ_FILTER+=' | .ip6tables = true | .experimental = true'
|
||||||
|
fi
|
||||||
|
|
||||||
|
jq "$JQ_FILTER" "$DOCKER_DAEMON_CONFIG" >"$TMP" && mv "$TMP" "$DOCKER_DAEMON_CONFIG"
|
||||||
|
echo -e "${LIGHT_GREEN}daemon.json updated. Restarting Docker...${NC}"
|
||||||
|
(command -v systemctl &>/dev/null && systemctl restart docker) || service docker restart
|
||||||
|
echo -e "${YELLOW}Docker restarted.${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Please install jq or manually update daemon.json and restart Docker.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}User declined Docker update – skipping Docker daemon configuration.${NC}"
|
||||||
|
echo -e "${YELLOW}IPv6 will be disabled for mailcow.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}If you change your mind later, please insert these changes manually to $DOCKER_DAEMON_CONFIG:${NC}"
|
||||||
|
echo "${MISSING[*]}"
|
||||||
|
echo ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
# Create new daemon.json if missing
|
||||||
|
if [[ -n "$FORCE" ]]; then
|
||||||
|
ans=Y
|
||||||
|
else
|
||||||
|
read -p "$DOCKER_DAEMON_CONFIG not found. Create it with IPv6 settings? [Y/n] " ans
|
||||||
|
ans=${ans:-Y}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ans =~ ^[Yy]$ ]]; then
|
||||||
|
mkdir -p "$(dirname "$DOCKER_DAEMON_CONFIG")"
|
||||||
|
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]]; then
|
||||||
|
cat > "$DOCKER_DAEMON_CONFIG" <<EOF
|
||||||
|
{
|
||||||
|
"ipv6": true,
|
||||||
|
"fixed-cidr-v6": "fd00:dead:beef:c0::/80",
|
||||||
|
"ip6tables": true,
|
||||||
|
"experimental": true
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
elif [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then
|
||||||
|
cat > "$DOCKER_DAEMON_CONFIG" <<EOF
|
||||||
|
{
|
||||||
|
"ipv6": true,
|
||||||
|
"fixed-cidr-v6": "fd00:dead:beef:c0::/80"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
# Docker 28+: ipv6 works without fixed-cidr-v6
|
||||||
|
cat > "$DOCKER_DAEMON_CONFIG" <<EOF
|
||||||
|
{
|
||||||
|
"ipv6": true
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}Created $DOCKER_DAEMON_CONFIG with IPv6 settings.${NC}"
|
||||||
|
echo "Restarting Docker..."
|
||||||
|
(command -v systemctl &>/dev/null && systemctl restart docker) || service docker restart
|
||||||
|
echo "Docker restarted."
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}User declined to create daemon.json – skipping Docker daemon configuration.${NC}"
|
||||||
|
echo -e "${YELLOW}IPv6 will be disabled for mailcow.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}If you change your mind later, please create $DOCKER_DAEMON_CONFIG with these settings:${NC}"
|
||||||
|
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]]; then
|
||||||
|
echo ' "ipv6": true,'
|
||||||
|
echo ' "fixed-cidr-v6": "fd00:dead:beef:c0::/80",'
|
||||||
|
echo ' "ip6tables": true,'
|
||||||
|
echo ' "experimental": true'
|
||||||
|
elif [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then
|
||||||
|
echo ' "ipv6": true,'
|
||||||
|
echo ' "fixed-cidr-v6": "fd00:dead:beef:c0::/80"'
|
||||||
|
else
|
||||||
|
echo ' "ipv6": true'
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3) Main wrapper for generate_config.sh and update.sh
|
||||||
|
configure_ipv6() {
|
||||||
|
# detect manual override if mailcow.conf is present
|
||||||
|
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]] && grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
|
||||||
|
MANUAL_SETTING=$(grep '^ENABLE_IPV6=' "$MAILCOW_CONF" | cut -d= -f2)
|
||||||
|
elif [[ -z "$MAILCOW_CONF" ]] && [[ -n "${ENABLE_IPV6:-}" ]]; then
|
||||||
|
MANUAL_SETTING="$ENABLE_IPV6"
|
||||||
|
else
|
||||||
|
MANUAL_SETTING=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
get_ipv6_support
|
||||||
|
|
||||||
|
# if user manually set it, check for mismatch
|
||||||
|
if [[ "$DETECTED_IPV6" != "true" ]]; then
|
||||||
|
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then
|
||||||
|
if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
|
||||||
|
sed -i 's/^ENABLE_IPV6=.*/ENABLE_IPV6=false/' "$MAILCOW_CONF"
|
||||||
|
else
|
||||||
|
echo "ENABLE_IPV6=false" >> "$MAILCOW_CONF"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
export IPV6_BOOL=false
|
||||||
|
fi
|
||||||
|
echo "Skipping Docker IPv6 configuration because host does not support IPv6."
|
||||||
|
echo "Make sure to check if your docker daemon.json does not include \"enable_ipv6\": true if you do not want IPv6."
|
||||||
|
echo "IPv6 configuration complete: ENABLE_IPV6=false"
|
||||||
|
sleep 2
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker_daemon_edit; then
|
||||||
|
# User declined Docker daemon configuration
|
||||||
|
# When called from update.sh, MAILCOW_CONF is set and we modify the existing file
|
||||||
|
# When called from generate_config.sh, MAILCOW_CONF is not set and we export IPV6_BOOL
|
||||||
|
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then
|
||||||
|
if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
|
||||||
|
sed -i 's/^ENABLE_IPV6=.*/ENABLE_IPV6=false/' "$MAILCOW_CONF"
|
||||||
|
else
|
||||||
|
echo "ENABLE_IPV6=false" >> "$MAILCOW_CONF"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
export IPV6_BOOL=false
|
||||||
|
fi
|
||||||
|
echo "IPv6 configuration complete: ENABLE_IPV6=false"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then
|
||||||
|
if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
|
||||||
|
sed -i 's/^ENABLE_IPV6=.*/ENABLE_IPV6=true/' "$MAILCOW_CONF"
|
||||||
|
else
|
||||||
|
echo "ENABLE_IPV6=true" >> "$MAILCOW_CONF"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
export IPV6_BOOL=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "IPv6 configuration complete: ENABLE_IPV6=true"
|
||||||
|
}
|
||||||
96
_modules/scripts/migrate_options.sh
Normal file
96
_modules/scripts/migrate_options.sh
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# _modules/scripts/migrate_options.sh
|
||||||
|
# THIS SCRIPT IS DESIGNED TO BE RUNNING BY MAILCOW SCRIPTS ONLY!
|
||||||
|
# DO NOT, AGAIN, NOT TRY TO RUN THIS SCRIPT STANDALONE!!!!!!
|
||||||
|
|
||||||
|
migrate_config_options() {
|
||||||
|
|
||||||
|
sed -i --follow-symlinks '$a\' mailcow.conf
|
||||||
|
|
||||||
|
KEYS=(
|
||||||
|
SOLR_HEAP
|
||||||
|
SKIP_SOLR
|
||||||
|
SOLR_PORT
|
||||||
|
FLATCURVE_EXPERIMENTAL
|
||||||
|
DISABLE_IPv6
|
||||||
|
ACME_CONTACT
|
||||||
|
)
|
||||||
|
|
||||||
|
for key in "${KEYS[@]}"; do
|
||||||
|
if grep -q "${key}" mailcow.conf; then
|
||||||
|
case "${key}" in
|
||||||
|
SOLR_HEAP)
|
||||||
|
echo "Removing ${key} in mailcow.conf"
|
||||||
|
sed -i '/# Solr heap size in MB\b/d' mailcow.conf
|
||||||
|
sed -i '/# Solr is a prone to run\b/d' mailcow.conf
|
||||||
|
sed -i '/SOLR_HEAP\b/d' mailcow.conf
|
||||||
|
;;
|
||||||
|
SKIP_SOLR)
|
||||||
|
echo "Removing ${key} in mailcow.conf"
|
||||||
|
sed -i '/\bSkip Solr on low-memory\b/d' mailcow.conf
|
||||||
|
sed -i '/\bSolr is disabled by default\b/d' mailcow.conf
|
||||||
|
sed -i '/\bDisable Solr or\b/d' mailcow.conf
|
||||||
|
sed -i '/\bSKIP_SOLR\b/d' mailcow.conf
|
||||||
|
;;
|
||||||
|
SOLR_PORT)
|
||||||
|
echo "Removing ${key} in mailcow.conf"
|
||||||
|
sed -i '/\bSOLR_PORT\b/d' mailcow.conf
|
||||||
|
;;
|
||||||
|
FLATCURVE_EXPERIMENTAL)
|
||||||
|
echo "Removing ${key} in mailcow.conf"
|
||||||
|
sed -i '/\bFLATCURVE_EXPERIMENTAL\b/d' mailcow.conf
|
||||||
|
;;
|
||||||
|
DISABLE_IPv6)
|
||||||
|
echo "Migrating ${key} to ENABLE_IPv6 in mailcow.conf"
|
||||||
|
local old=$(grep '^DISABLE_IPv6=' "mailcow.conf" | cut -d'=' -f2)
|
||||||
|
local new
|
||||||
|
if [[ "$old" == "y" ]]; then
|
||||||
|
new="false"
|
||||||
|
else
|
||||||
|
new="true"
|
||||||
|
fi
|
||||||
|
sed -i '/^DISABLE_IPv6=/d' "mailcow.conf"
|
||||||
|
echo "ENABLE_IPV6=$new" >> "mailcow.conf"
|
||||||
|
;;
|
||||||
|
ACME_CONTACT)
|
||||||
|
echo "Deleting obsoleted ${key} in mailcow.conf"
|
||||||
|
sed -i '/^# Lets Encrypt registration contact information/d' mailcow.conf
|
||||||
|
sed -i '/^# Optional: Leave empty for none/d' mailcow.conf
|
||||||
|
sed -i '/^# This value is only used on first order!/d' mailcow.conf
|
||||||
|
sed -i '/^# Setting it at a later point will require the following steps:/d' mailcow.conf
|
||||||
|
sed -i '/^# https:\/\/docs.mailcow.email\/troubleshooting\/debug-reset_tls\//d' mailcow.conf
|
||||||
|
sed -i '/^ACME_CONTACT=.*/d' mailcow.conf
|
||||||
|
sed -i '/^#ACME_CONTACT=.*/d' mailcow.conf
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
solr_volume=$(docker volume ls -qf name=^${COMPOSE_PROJECT_NAME}_solr-vol-1)
|
||||||
|
if [[ -n $solr_volume ]]; then
|
||||||
|
echo -e "\e[34mSolr has been replaced within mailcow since 2025-01.\nThe volume $solr_volume is unused.\e[0m"
|
||||||
|
sleep 1
|
||||||
|
if [ ! "$FORCE" ]; then
|
||||||
|
read -r -p "Remove $solr_volume? [y/N] " response
|
||||||
|
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
|
echo -e "\e[33mRemoving $solr_volume...\e[0m"
|
||||||
|
docker volume rm $solr_volume || echo -e "\e[31mFailed to remove. Remove it manually!\e[0m"
|
||||||
|
echo -e "\e[32mSuccessfully removed $solr_volume!\e[0m"
|
||||||
|
else
|
||||||
|
echo -e "Not removing $solr_volume. Run \`docker volume rm $solr_volume\` manually if needed."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "\e[33mForce removing $solr_volume...\e[0m"
|
||||||
|
docker volume rm $solr_volume || echo -e "\e[31mFailed to remove. Remove it manually!\e[0m"
|
||||||
|
echo -e "\e[32mSuccessfully removed $solr_volume!\e[0m"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delete old fts.conf before forced switch to flatcurve to ensure update is working properly
|
||||||
|
FTS_CONF_PATH="${SCRIPT_DIR}/data/conf/dovecot/conf.d/fts.conf"
|
||||||
|
if [[ -f "$FTS_CONF_PATH" ]]; then
|
||||||
|
if grep -q "Autogenerated by mailcow" "$FTS_CONF_PATH"; then
|
||||||
|
rm -rf $FTS_CONF_PATH
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
300
_modules/scripts/new_options.sh
Normal file
300
_modules/scripts/new_options.sh
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# _modules/scripts/new_options.sh
|
||||||
|
# THIS SCRIPT IS DESIGNED TO BE RUNNING BY MAILCOW SCRIPTS ONLY!
|
||||||
|
# DO NOT, AGAIN, NOT TRY TO RUN THIS SCRIPT STANDALONE!!!!!!
|
||||||
|
|
||||||
|
adapt_new_options() {
|
||||||
|
|
||||||
|
CONFIG_ARRAY=(
|
||||||
|
"AUTODISCOVER_SAN"
|
||||||
|
"SKIP_LETS_ENCRYPT"
|
||||||
|
"SKIP_SOGO"
|
||||||
|
"USE_WATCHDOG"
|
||||||
|
"WATCHDOG_NOTIFY_EMAIL"
|
||||||
|
"WATCHDOG_NOTIFY_WEBHOOK"
|
||||||
|
"WATCHDOG_NOTIFY_WEBHOOK_BODY"
|
||||||
|
"WATCHDOG_NOTIFY_BAN"
|
||||||
|
"WATCHDOG_NOTIFY_START"
|
||||||
|
"WATCHDOG_EXTERNAL_CHECKS"
|
||||||
|
"WATCHDOG_SUBJECT"
|
||||||
|
"SKIP_CLAMD"
|
||||||
|
"SKIP_OLEFY"
|
||||||
|
"SKIP_IP_CHECK"
|
||||||
|
"ADDITIONAL_SAN"
|
||||||
|
"DOVEADM_PORT"
|
||||||
|
"IPV4_NETWORK"
|
||||||
|
"IPV6_NETWORK"
|
||||||
|
"LOG_LINES"
|
||||||
|
"SNAT_TO_SOURCE"
|
||||||
|
"SNAT6_TO_SOURCE"
|
||||||
|
"COMPOSE_PROJECT_NAME"
|
||||||
|
"DOCKER_COMPOSE_VERSION"
|
||||||
|
"SQL_PORT"
|
||||||
|
"API_KEY"
|
||||||
|
"API_KEY_READ_ONLY"
|
||||||
|
"API_ALLOW_FROM"
|
||||||
|
"MAILDIR_GC_TIME"
|
||||||
|
"MAILDIR_SUB"
|
||||||
|
"ACL_ANYONE"
|
||||||
|
"FTS_HEAP"
|
||||||
|
"FTS_PROCS"
|
||||||
|
"SKIP_FTS"
|
||||||
|
"ENABLE_SSL_SNI"
|
||||||
|
"ALLOW_ADMIN_EMAIL_LOGIN"
|
||||||
|
"SKIP_HTTP_VERIFICATION"
|
||||||
|
"SOGO_EXPIRE_SESSION"
|
||||||
|
"SOGO_URL_ENCRYPTION_KEY"
|
||||||
|
"REDIS_PORT"
|
||||||
|
"REDISPASS"
|
||||||
|
"DOVECOT_MASTER_USER"
|
||||||
|
"DOVECOT_MASTER_PASS"
|
||||||
|
"MAILCOW_PASS_SCHEME"
|
||||||
|
"ADDITIONAL_SERVER_NAMES"
|
||||||
|
"WATCHDOG_VERBOSE"
|
||||||
|
"WEBAUTHN_ONLY_TRUSTED_VENDORS"
|
||||||
|
"SPAMHAUS_DQS_KEY"
|
||||||
|
"SKIP_UNBOUND_HEALTHCHECK"
|
||||||
|
"DISABLE_NETFILTER_ISOLATION_RULE"
|
||||||
|
"HTTP_REDIRECT"
|
||||||
|
"ENABLE_IPV6"
|
||||||
|
)
|
||||||
|
|
||||||
|
sed -i --follow-symlinks '$a\' mailcow.conf
|
||||||
|
for option in ${CONFIG_ARRAY[@]}; do
|
||||||
|
if grep -q "${option}" mailcow.conf; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Adding new option \"${option}\" to mailcow.conf"
|
||||||
|
|
||||||
|
case "${option}" in
|
||||||
|
AUTODISCOVER_SAN)
|
||||||
|
echo '# Obtain certificates for autodiscover.* and autoconfig.* domains.' >> mailcow.conf
|
||||||
|
echo '# This can be useful to switch off in case you are in a scenario where a reverse proxy already handles those.' >> mailcow.conf
|
||||||
|
echo '# There are mixed scenarios where ports 80,443 are occupied and you do not want to share certs' >> mailcow.conf
|
||||||
|
echo '# between services. So acme-mailcow obtains for maildomains and all web-things get handled' >> mailcow.conf
|
||||||
|
echo '# in the reverse proxy.' >> mailcow.conf
|
||||||
|
echo 'AUTODISCOVER_SAN=y' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
|
||||||
|
DOCKER_COMPOSE_VERSION)
|
||||||
|
echo "# Used Docker Compose version" >> mailcow.conf
|
||||||
|
echo "# Switch here between native (compose plugin) and standalone" >> mailcow.conf
|
||||||
|
echo "# For more informations take a look at the mailcow docs regarding the configuration options." >> mailcow.conf
|
||||||
|
echo "# Normally this should be untouched but if you decided to use either of those you can switch it manually here." >> mailcow.conf
|
||||||
|
echo "# Please be aware that at least one of those variants should be installed on your machine or mailcow will fail." >> mailcow.conf
|
||||||
|
echo "" >> mailcow.conf
|
||||||
|
echo "DOCKER_COMPOSE_VERSION=${DOCKER_COMPOSE_VERSION}" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
|
||||||
|
DOVEADM_PORT)
|
||||||
|
echo "DOVEADM_PORT=127.0.0.1:19991" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
|
||||||
|
LOG_LINES)
|
||||||
|
echo '# Max log lines per service to keep in Redis logs' >> mailcow.conf
|
||||||
|
echo "LOG_LINES=9999" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
IPV4_NETWORK)
|
||||||
|
echo '# Internal IPv4 /24 subnet, format n.n.n. (expands to n.n.n.0/24)' >> mailcow.conf
|
||||||
|
echo "IPV4_NETWORK=172.22.1" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
IPV6_NETWORK)
|
||||||
|
echo '# Internal IPv6 subnet in fc00::/7' >> mailcow.conf
|
||||||
|
echo "IPV6_NETWORK=fd4d:6169:6c63:6f77::/64" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SQL_PORT)
|
||||||
|
echo '# Bind SQL to 127.0.0.1 on port 13306' >> mailcow.conf
|
||||||
|
echo "SQL_PORT=127.0.0.1:13306" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
API_KEY)
|
||||||
|
echo '# Create or override API key for web UI' >> mailcow.conf
|
||||||
|
echo "#API_KEY=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
API_KEY_READ_ONLY)
|
||||||
|
echo '# Create or override read-only API key for web UI' >> mailcow.conf
|
||||||
|
echo "#API_KEY_READ_ONLY=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
API_ALLOW_FROM)
|
||||||
|
echo '# Must be set for API_KEY to be active' >> mailcow.conf
|
||||||
|
echo '# IPs only, no networks (networks can be set via UI)' >> mailcow.conf
|
||||||
|
echo "#API_ALLOW_FROM=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SNAT_TO_SOURCE)
|
||||||
|
echo '# Use this IPv4 for outgoing connections (SNAT)' >> mailcow.conf
|
||||||
|
echo "#SNAT_TO_SOURCE=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SNAT6_TO_SOURCE)
|
||||||
|
echo '# Use this IPv6 for outgoing connections (SNAT)' >> mailcow.conf
|
||||||
|
echo "#SNAT6_TO_SOURCE=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
MAILDIR_GC_TIME)
|
||||||
|
echo '# Garbage collector cleanup' >> mailcow.conf
|
||||||
|
echo '# Deleted domains and mailboxes are moved to /var/vmail/_garbage/timestamp_sanitizedstring' >> mailcow.conf
|
||||||
|
echo '# How long should objects remain in the garbage until they are being deleted? (value in minutes)' >> mailcow.conf
|
||||||
|
echo '# Check interval is hourly' >> mailcow.conf
|
||||||
|
echo 'MAILDIR_GC_TIME=1440' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
ACL_ANYONE)
|
||||||
|
echo '# Set this to "allow" to enable the anyone pseudo user. Disabled by default.' >> mailcow.conf
|
||||||
|
echo '# When enabled, ACL can be created, that apply to "All authenticated users"' >> mailcow.conf
|
||||||
|
echo '# This should probably only be activated on mail hosts, that are used exclusively by one organisation.' >> mailcow.conf
|
||||||
|
echo '# Otherwise a user might share data with too many other users.' >> mailcow.conf
|
||||||
|
echo 'ACL_ANYONE=disallow' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
FTS_HEAP)
|
||||||
|
echo '# Dovecot Indexing (FTS) Process maximum heap size in MB, there is no recommendation, please see Dovecot docs.' >> mailcow.conf
|
||||||
|
echo '# Flatcurve is used as FTS Engine. It is supposed to be pretty efficient in CPU and RAM consumption.' >> mailcow.conf
|
||||||
|
echo '# Please always monitor your Resource consumption!' >> mailcow.conf
|
||||||
|
echo "FTS_HEAP=128" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SKIP_FTS)
|
||||||
|
echo '# Skip FTS (Fulltext Search) for Dovecot on low-memory, low-threaded systems or if you simply want to disable it.' >> mailcow.conf
|
||||||
|
echo "# Dovecot inside mailcow use Flatcurve as FTS Backend." >> mailcow.conf
|
||||||
|
echo "SKIP_FTS=y" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
FTS_PROCS)
|
||||||
|
echo '# Controls how many processes the Dovecot indexing process can spawn at max.' >> mailcow.conf
|
||||||
|
echo '# Too many indexing processes can use a lot of CPU and Disk I/O' >> mailcow.conf
|
||||||
|
echo '# Please visit: https://doc.dovecot.org/configuration_manual/service_configuration/#indexer-worker for more informations' >> mailcow.conf
|
||||||
|
echo "FTS_PROCS=1" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
ENABLE_SSL_SNI)
|
||||||
|
echo '# Create seperate certificates for all domains - y/n' >> mailcow.conf
|
||||||
|
echo '# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames' >> mailcow.conf
|
||||||
|
echo '# see https://wiki.dovecot.org/SSL/SNIClientSupport' >> mailcow.conf
|
||||||
|
echo "ENABLE_SSL_SNI=n" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SKIP_SOGO)
|
||||||
|
echo '# Skip SOGo: Will disable SOGo integration and therefore webmail, DAV protocols and ActiveSync support (experimental, unsupported, not fully implemented) - y/n' >> mailcow.conf
|
||||||
|
echo "SKIP_SOGO=n" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
MAILDIR_SUB)
|
||||||
|
echo '# MAILDIR_SUB defines a path in a users virtual home to keep the maildir in. Leave empty for updated setups.' >> mailcow.conf
|
||||||
|
echo "#MAILDIR_SUB=Maildir" >> mailcow.conf
|
||||||
|
echo "MAILDIR_SUB=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
WATCHDOG_NOTIFY_WEBHOOK)
|
||||||
|
echo '# Send notifications to a webhook URL that receives a POST request with the content type "application/json".' >> mailcow.conf
|
||||||
|
echo '# You can use this to send notifications to services like Discord, Slack and others.' >> mailcow.conf
|
||||||
|
echo '#WATCHDOG_NOTIFY_WEBHOOK=https://discord.com/api/webhooks/XXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
WATCHDOG_NOTIFY_WEBHOOK_BODY)
|
||||||
|
echo '# JSON body included in the webhook POST request. Needs to be in single quotes.' >> mailcow.conf
|
||||||
|
echo '# Following variables are available: SUBJECT, BODY' >> mailcow.conf
|
||||||
|
WEBHOOK_BODY='{"username": "mailcow Watchdog", "content": "**${SUBJECT}**\n${BODY}"}'
|
||||||
|
echo "#WATCHDOG_NOTIFY_WEBHOOK_BODY='${WEBHOOK_BODY}'" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
WATCHDOG_NOTIFY_BAN)
|
||||||
|
echo '# Notify about banned IP. Includes whois lookup.' >> mailcow.conf
|
||||||
|
echo "WATCHDOG_NOTIFY_BAN=y" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
WATCHDOG_NOTIFY_START)
|
||||||
|
echo '# Send a notification when the watchdog is started.' >> mailcow.conf
|
||||||
|
echo "WATCHDOG_NOTIFY_START=y" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
WATCHDOG_SUBJECT)
|
||||||
|
echo '# Subject for watchdog mails. Defaults to "Watchdog ALERT" followed by the error message.' >> mailcow.conf
|
||||||
|
echo "#WATCHDOG_SUBJECT=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
WATCHDOG_EXTERNAL_CHECKS)
|
||||||
|
echo '# Checks if mailcow is an open relay. Requires a SAL. More checks will follow.' >> mailcow.conf
|
||||||
|
echo '# No data is collected. Opt-in and anonymous.' >> mailcow.conf
|
||||||
|
echo '# Will only work with unmodified mailcow setups.' >> mailcow.conf
|
||||||
|
echo "WATCHDOG_EXTERNAL_CHECKS=n" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SOGO_EXPIRE_SESSION)
|
||||||
|
echo '# SOGo session timeout in minutes' >> mailcow.conf
|
||||||
|
echo "SOGO_EXPIRE_SESSION=480" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
REDIS_PORT)
|
||||||
|
echo "REDIS_PORT=127.0.0.1:7654" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
DOVECOT_MASTER_USER)
|
||||||
|
echo '# DOVECOT_MASTER_USER and _PASS must _both_ be provided. No special chars.' >> mailcow.conf
|
||||||
|
echo '# Empty by default to auto-generate master user and password on start.' >> mailcow.conf
|
||||||
|
echo '# User expands to DOVECOT_MASTER_USER@mailcow.local' >> mailcow.conf
|
||||||
|
echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
|
||||||
|
echo "DOVECOT_MASTER_USER=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
DOVECOT_MASTER_PASS)
|
||||||
|
echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
|
||||||
|
echo "DOVECOT_MASTER_PASS=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
MAILCOW_PASS_SCHEME)
|
||||||
|
echo '# Password hash algorithm' >> mailcow.conf
|
||||||
|
echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf
|
||||||
|
echo '# see https://docs.mailcow.email/models/model-passwd/' >> mailcow.conf
|
||||||
|
echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
ADDITIONAL_SERVER_NAMES)
|
||||||
|
echo '# Additional server names for mailcow UI' >> mailcow.conf
|
||||||
|
echo '#' >> mailcow.conf
|
||||||
|
echo '# Specify alternative addresses for the mailcow UI to respond to' >> mailcow.conf
|
||||||
|
echo '# This is useful when you set mail.* as ADDITIONAL_SAN and want to make sure mail.maildomain.com will always point to the mailcow UI.' >> mailcow.conf
|
||||||
|
echo '# If the server name does not match a known site, Nginx decides by best-guess and may redirect users to the wrong web root.' >> mailcow.conf
|
||||||
|
echo '# You can understand this as server_name directive in Nginx.' >> mailcow.conf
|
||||||
|
echo '# Comma separated list without spaces! Example: ADDITIONAL_SERVER_NAMES=a.b.c,d.e.f' >> mailcow.conf
|
||||||
|
echo 'ADDITIONAL_SERVER_NAMES=' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
WEBAUTHN_ONLY_TRUSTED_VENDORS)
|
||||||
|
echo "# WebAuthn device manufacturer verification" >> mailcow.conf
|
||||||
|
echo '# After setting WEBAUTHN_ONLY_TRUSTED_VENDORS=y only devices from trusted manufacturers are allowed' >> mailcow.conf
|
||||||
|
echo '# root certificates can be placed for validation under mailcow-dockerized/data/web/inc/lib/WebAuthn/rootCertificates' >> mailcow.conf
|
||||||
|
echo 'WEBAUTHN_ONLY_TRUSTED_VENDORS=n' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SPAMHAUS_DQS_KEY)
|
||||||
|
echo "# Spamhaus Data Query Service Key" >> mailcow.conf
|
||||||
|
echo '# Optional: Leave empty for none' >> mailcow.conf
|
||||||
|
echo '# Enter your key here if you are using a blocked ASN (OVH, AWS, Cloudflare e.g) for the unregistered Spamhaus Blocklist.' >> mailcow.conf
|
||||||
|
echo '# If empty, it will completely disable Spamhaus blocklists if it detects that you are running on a server using a blocked AS.' >> mailcow.conf
|
||||||
|
echo '# Otherwise it will work as usual.' >> mailcow.conf
|
||||||
|
echo 'SPAMHAUS_DQS_KEY=' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
WATCHDOG_VERBOSE)
|
||||||
|
echo '# Enable watchdog verbose logging' >> mailcow.conf
|
||||||
|
echo 'WATCHDOG_VERBOSE=n' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SKIP_UNBOUND_HEALTHCHECK)
|
||||||
|
echo '# Skip Unbound (DNS Resolver) Healthchecks (NOT Recommended!) - y/n' >> mailcow.conf
|
||||||
|
echo 'SKIP_UNBOUND_HEALTHCHECK=n' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
DISABLE_NETFILTER_ISOLATION_RULE)
|
||||||
|
echo '# Prevent netfilter from setting an iptables/nftables rule to isolate the mailcow docker network - y/n' >> mailcow.conf
|
||||||
|
echo '# CAUTION: Disabling this may expose container ports to other neighbors on the same subnet, even if the ports are bound to localhost' >> mailcow.conf
|
||||||
|
echo 'DISABLE_NETFILTER_ISOLATION_RULE=n' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
HTTP_REDIRECT)
|
||||||
|
echo '# Redirect HTTP connections to HTTPS - y/n' >> mailcow.conf
|
||||||
|
echo 'HTTP_REDIRECT=n' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
ENABLE_IPV6)
|
||||||
|
echo '# IPv6 Controller Section' >> mailcow.conf
|
||||||
|
echo '# This variable controls the usage of IPv6 within mailcow.' >> mailcow.conf
|
||||||
|
echo '# Can either be true or false | Defaults to true' >> mailcow.conf
|
||||||
|
echo '# WARNING: MAKE SURE TO PROPERLY CONFIGURE IPv6 ON YOUR HOST FIRST BEFORE ENABLING THIS AS FAULTY CONFIGURATIONS CAN LEAD TO OPEN RELAYS!' >> mailcow.conf
|
||||||
|
echo '# A COMPLETE DOCKER STACK REBUILD (compose down && compose up -d) IS NEEDED TO APPLY THIS.' >> mailcow.conf
|
||||||
|
echo ENABLE_IPV6=${IPV6_BOOL} >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SKIP_CLAMD)
|
||||||
|
echo '# Skip ClamAV (clamd-mailcow) anti-virus (Rspamd will auto-detect a missing ClamAV container) - y/n' >> mailcow.conf
|
||||||
|
echo 'SKIP_CLAMD=n' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SKIP_OLEFY)
|
||||||
|
echo '# Skip Olefy (olefy-mailcow) anti-virus for Office documents (Rspamd will auto-detect a missing Olefy container) - y/n' >> mailcow.conf
|
||||||
|
echo 'SKIP_OLEFY=n' >> mailcow.conf
|
||||||
|
;;
|
||||||
|
REDISPASS)
|
||||||
|
echo "REDISPASS=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 2>/dev/null | head -c 28)" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
SOGO_URL_ENCRYPTION_KEY)
|
||||||
|
echo '# SOGo URL encryption key (exactly 16 characters, limited to A–Z, a–z, 0–9)' >> mailcow.conf
|
||||||
|
echo '# This key is used to encrypt email addresses within SOGo URLs' >> mailcow.conf
|
||||||
|
echo "SOGO_URL_ENCRYPTION_KEY=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 2>/dev/null | head -c 16)" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "${option}=" >> mailcow.conf
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
@@ -159,18 +159,6 @@ while true; do
|
|||||||
fi
|
fi
|
||||||
if [[ ! -f ${ACME_BASE}/acme/account.pem ]]; then
|
if [[ ! -f ${ACME_BASE}/acme/account.pem ]]; then
|
||||||
log_f "Generating missing Lets Encrypt account key..."
|
log_f "Generating missing Lets Encrypt account key..."
|
||||||
if [[ ! -z ${ACME_CONTACT} ]]; then
|
|
||||||
if ! verify_email "${ACME_CONTACT}"; then
|
|
||||||
log_f "Invalid email address, will not start registration!"
|
|
||||||
sleep 365d
|
|
||||||
exec $(readlink -f "$0")
|
|
||||||
else
|
|
||||||
ACME_CONTACT_PARAMETER="--contact mailto:${ACME_CONTACT}"
|
|
||||||
log_f "Valid email address, using ${ACME_CONTACT} for registration"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
ACME_CONTACT_PARAMETER=""
|
|
||||||
fi
|
|
||||||
openssl genrsa 4096 > ${ACME_BASE}/acme/account.pem
|
openssl genrsa 4096 > ${ACME_BASE}/acme/account.pem
|
||||||
else
|
else
|
||||||
log_f "Using existing Lets Encrypt account key ${ACME_BASE}/acme/account.pem"
|
log_f "Using existing Lets Encrypt account key ${ACME_BASE}/acme/account.pem"
|
||||||
@@ -218,7 +206,7 @@ while true; do
|
|||||||
|
|
||||||
if [[ ${AUTODISCOVER_SAN} == "y" ]]; then
|
if [[ ${AUTODISCOVER_SAN} == "y" ]]; then
|
||||||
# Fetch certs for autoconfig and autodiscover subdomains
|
# Fetch certs for autoconfig and autodiscover subdomains
|
||||||
ADDITIONAL_WC_ARR+=('autodiscover' 'autoconfig')
|
ADDITIONAL_WC_ARR+=('autodiscover' 'autoconfig' 'mta-sts')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${SKIP_IP_CHECK} != "y" ]]; then
|
if [[ ${SKIP_IP_CHECK} != "y" ]]; then
|
||||||
@@ -299,7 +287,7 @@ while true; do
|
|||||||
VALIDATED_CERTIFICATES+=("${CERT_NAME}")
|
VALIDATED_CERTIFICATES+=("${CERT_NAME}")
|
||||||
|
|
||||||
# obtain server certificate if required
|
# obtain server certificate if required
|
||||||
ACME_CONTACT_PARAMETER=${ACME_CONTACT_PARAMETER} DOMAINS=${SERVER_SAN_VALIDATED[@]} /srv/obtain-certificate.sh rsa
|
DOMAINS=${SERVER_SAN_VALIDATED[@]} /srv/obtain-certificate.sh rsa
|
||||||
RETURN="$?"
|
RETURN="$?"
|
||||||
if [[ "$RETURN" == "0" ]]; then # 0 = cert created successfully
|
if [[ "$RETURN" == "0" ]]; then # 0 = cert created successfully
|
||||||
CERT_AMOUNT_CHANGED=1
|
CERT_AMOUNT_CHANGED=1
|
||||||
|
|||||||
@@ -93,8 +93,8 @@ until dig letsencrypt.org +time=3 +tries=1 @unbound > /dev/null; do
|
|||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
log_f "Resolver OK"
|
log_f "Resolver OK"
|
||||||
log_f "Using command acme-tiny ${DIRECTORY_URL} ${ACME_CONTACT_PARAMETER} --account-key ${ACME_BASE}/acme/account.pem --disable-check --csr ${CSR} --acme-dir /var/www/acme/"
|
log_f "Using command acme-tiny ${DIRECTORY_URL} --account-key ${ACME_BASE}/acme/account.pem --disable-check --csr ${CSR} --acme-dir /var/www/acme/"
|
||||||
ACME_RESPONSE=$(acme-tiny ${DIRECTORY_URL} ${ACME_CONTACT_PARAMETER} \
|
ACME_RESPONSE=$(acme-tiny ${DIRECTORY_URL} \
|
||||||
--account-key ${ACME_BASE}/acme/account.pem \
|
--account-key ${ACME_BASE}/acme/account.pem \
|
||||||
--disable-check \
|
--disable-check \
|
||||||
--csr ${CSR} \
|
--csr ${CSR} \
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
FROM debian:bookworm-slim
|
FROM debian:trixie-slim
|
||||||
|
|
||||||
RUN apt update && apt install pigz -y --no-install-recommends
|
RUN apt update && apt install pigz zstd -y --no-install-recommends
|
||||||
@@ -8,7 +8,7 @@ fi
|
|||||||
|
|
||||||
# Cleaning up garbage
|
# Cleaning up garbage
|
||||||
echo "Cleaning up tmp files..."
|
echo "Cleaning up tmp files..."
|
||||||
rm -rf /var/lib/clamav/clamav-*.tmp
|
rm -rf /var/lib/clamav/tmp.*
|
||||||
|
|
||||||
# Prepare whitelist
|
# Prepare whitelist
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ FROM alpine:3.21
|
|||||||
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||||
|
|
||||||
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
|
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
|
||||||
ARG GOSU_VERSION=1.16
|
ARG GOSU_VERSION=1.17
|
||||||
|
|
||||||
ENV LANG=C.UTF-8
|
ENV LANG=C.UTF-8
|
||||||
ENV LC_ALL=C.UTF-8
|
ENV LC_ALL=C.UTF-8
|
||||||
|
|||||||
@@ -204,16 +204,17 @@ EOF
|
|||||||
# Create random master Password for SOGo SSO
|
# Create random master Password for SOGo SSO
|
||||||
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
|
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
|
||||||
echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
|
echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
|
||||||
# Creating additional creds file for SOGo notify crons (calendars, etc)
|
|
||||||
echo -n ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/cron.creds
|
|
||||||
cat <<EOF > /etc/dovecot/sogo-sso.conf
|
cat <<EOF > /etc/dovecot/sogo-sso.conf
|
||||||
# Autogenerated by mailcow
|
# Autogenerated by mailcow
|
||||||
passdb {
|
passdb {
|
||||||
driver = static
|
driver = static
|
||||||
args = allow_real_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
|
args = allow_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# Creating additional creds file for SOGo notify crons (calendars, etc) (dummy user, sso password)
|
||||||
|
echo -n ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/cron.creds
|
||||||
|
|
||||||
if [[ "${MASTER}" =~ ^([nN][oO]|[nN])+$ ]]; then
|
if [[ "${MASTER}" =~ ^([nN][oO]|[nN])+$ ]]; then
|
||||||
# Toggling MASTER will result in a rebuild of containers, so the quota script will be recreated
|
# Toggling MASTER will result in a rebuild of containers, so the quota script will be recreated
|
||||||
cat <<'EOF' > /usr/local/bin/quota_notify.py
|
cat <<'EOF' > /usr/local/bin/quota_notify.py
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ while ($row = $sth->fetchrow_arrayref()) {
|
|||||||
"--tmpdir", "/tmp",
|
"--tmpdir", "/tmp",
|
||||||
"--nofoldersizes",
|
"--nofoldersizes",
|
||||||
"--addheader",
|
"--addheader",
|
||||||
($timeout1 gt "0" ? () : ('--timeout1', $timeout1)),
|
($timeout1 le "0" ? () : ('--timeout1', $timeout1)),
|
||||||
($timeout2 gt "0" ? () : ('--timeout2', $timeout2)),
|
($timeout2 le "0" ? () : ('--timeout2', $timeout2)),
|
||||||
($exclude eq "" ? () : ("--exclude", $exclude)),
|
($exclude eq "" ? () : ("--exclude", $exclude)),
|
||||||
($subfolder2 eq "" ? () : ('--subfolder2', $subfolder2)),
|
($subfolder2 eq "" ? () : ('--subfolder2', $subfolder2)),
|
||||||
($maxage eq "0" ? () : ('--maxage', $maxage)),
|
($maxage eq "0" ? () : ('--maxage', $maxage)),
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ from email.mime.multipart import MIMEMultipart
|
|||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.utils import COMMASPACE, formatdate
|
from email.utils import COMMASPACE, formatdate
|
||||||
import jinja2
|
import jinja2
|
||||||
from jinja2 import Template
|
from jinja2 import TemplateError
|
||||||
|
from jinja2.sandbox import SandboxedEnvironment
|
||||||
import json
|
import json
|
||||||
import redis
|
import redis
|
||||||
import time
|
import time
|
||||||
@@ -75,22 +76,27 @@ try:
|
|||||||
|
|
||||||
def notify_rcpt(rcpt, msg_count, quarantine_acl, category):
|
def notify_rcpt(rcpt, msg_count, quarantine_acl, category):
|
||||||
if category == "add_header": category = "add header"
|
if category == "add_header": category = "add header"
|
||||||
meta_query = query_mysql('SELECT SHA2(CONCAT(id, qid), 256) AS qhash, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = "%s" AND score < %f AND (action = "%s" OR "all" = "%s")' % (rcpt, max_score, category, category))
|
meta_query = query_mysql('SELECT `qhash`, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = "%s" AND score < %f AND (action = "%s" OR "all" = "%s")' % (rcpt, max_score, category, category))
|
||||||
print("%s: %d of %d messages qualify for notification" % (rcpt, len(meta_query), msg_count))
|
print("%s: %d of %d messages qualify for notification" % (rcpt, len(meta_query), msg_count))
|
||||||
if len(meta_query) == 0:
|
if len(meta_query) == 0:
|
||||||
return
|
return
|
||||||
msg_count = len(meta_query)
|
msg_count = len(meta_query)
|
||||||
|
env = SandboxedEnvironment()
|
||||||
if r.get('Q_HTML'):
|
if r.get('Q_HTML'):
|
||||||
try:
|
try:
|
||||||
template = Template(r.get('Q_HTML'))
|
template = env.from_string(r.get('Q_HTML'))
|
||||||
except:
|
except Exception:
|
||||||
print("Error: Cannot parse quarantine template, falling back to default template.")
|
print("Error: Cannot parse quarantine template, falling back to default template.")
|
||||||
with open('/templates/quarantine.tpl') as file_:
|
with open('/templates/quarantine.tpl') as file_:
|
||||||
template = Template(file_.read())
|
template = env.from_string(file_.read())
|
||||||
else:
|
else:
|
||||||
with open('/templates/quarantine.tpl') as file_:
|
with open('/templates/quarantine.tpl') as file_:
|
||||||
template = Template(file_.read())
|
template = env.from_string(file_.read())
|
||||||
html = template.render(meta=meta_query, username=rcpt, counter=msg_count, hostname=mailcow_hostname, quarantine_acl=quarantine_acl)
|
try:
|
||||||
|
html = template.render(meta=meta_query, username=rcpt, counter=msg_count, hostname=mailcow_hostname, quarantine_acl=quarantine_acl)
|
||||||
|
except (jinja2.exceptions.SecurityError, TemplateError) as ex:
|
||||||
|
print(f"SecurityError or TemplateError in template rendering: {ex}")
|
||||||
|
return
|
||||||
text = html2text.html2text(html)
|
text = html2text.html2text(html)
|
||||||
count = 0
|
count = 0
|
||||||
while count < 15:
|
while count < 15:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from email.mime.multipart import MIMEMultipart
|
|||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.utils import COMMASPACE, formatdate
|
from email.utils import COMMASPACE, formatdate
|
||||||
import jinja2
|
import jinja2
|
||||||
from jinja2 import Template
|
from jinja2.sandbox import SandboxedEnvironment
|
||||||
import redis
|
import redis
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
@@ -33,16 +33,24 @@ while True:
|
|||||||
|
|
||||||
if r.get('QW_HTML'):
|
if r.get('QW_HTML'):
|
||||||
try:
|
try:
|
||||||
template = Template(r.get('QW_HTML'))
|
env = SandboxedEnvironment()
|
||||||
except:
|
template = env.from_string(r.get('QW_HTML'))
|
||||||
print("Error: Cannot parse quarantine template, falling back to default template.")
|
except Exception:
|
||||||
|
print("Error: Cannot parse quota template, falling back to default template.")
|
||||||
with open('/templates/quota.tpl') as file_:
|
with open('/templates/quota.tpl') as file_:
|
||||||
template = Template(file_.read())
|
env = SandboxedEnvironment()
|
||||||
|
template = env.from_string(file_.read())
|
||||||
else:
|
else:
|
||||||
with open('/templates/quota.tpl') as file_:
|
with open('/templates/quota.tpl') as file_:
|
||||||
template = Template(file_.read())
|
env = SandboxedEnvironment()
|
||||||
|
template = env.from_string(file_.read())
|
||||||
|
|
||||||
|
try:
|
||||||
|
html = template.render(username=username, percent=percent)
|
||||||
|
except (jinja2.exceptions.SecurityError, jinja2.TemplateError) as ex:
|
||||||
|
print(f"SecurityError or TemplateError in template rendering: {ex}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
html = template.render(username=username, percent=percent)
|
|
||||||
text = html2text.html2text(html)
|
text = html2text.html2text(html)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
backend=iptables
|
backend=nftables
|
||||||
|
|
||||||
nft list table ip filter &>/dev/null
|
nft list table ip filter &>/dev/null
|
||||||
nftables_found=$?
|
nftables_found=$?
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
@@ -20,10 +22,13 @@ from modules.Logger import Logger
|
|||||||
from modules.IPTables import IPTables
|
from modules.IPTables import IPTables
|
||||||
from modules.NFTables import NFTables
|
from modules.NFTables import NFTables
|
||||||
|
|
||||||
|
def logdebug(msg):
|
||||||
|
if DEBUG:
|
||||||
|
logger.logInfo("DEBUG: %s" % msg)
|
||||||
|
|
||||||
# globals
|
# Globals
|
||||||
WHITELIST = []
|
WHITELIST = []
|
||||||
BLACKLIST= []
|
BLACKLIST = []
|
||||||
bans = {}
|
bans = {}
|
||||||
quit_now = False
|
quit_now = False
|
||||||
exit_code = 0
|
exit_code = 0
|
||||||
@@ -33,12 +38,10 @@ r = None
|
|||||||
pubsub = None
|
pubsub = None
|
||||||
clear_before_quit = False
|
clear_before_quit = False
|
||||||
|
|
||||||
|
|
||||||
def refreshF2boptions():
|
def refreshF2boptions():
|
||||||
global f2boptions
|
global f2boptions
|
||||||
global quit_now
|
global quit_now
|
||||||
global exit_code
|
global exit_code
|
||||||
|
|
||||||
f2boptions = {}
|
f2boptions = {}
|
||||||
|
|
||||||
if not r.get('F2B_OPTIONS'):
|
if not r.get('F2B_OPTIONS'):
|
||||||
@@ -52,8 +55,9 @@ def refreshF2boptions():
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
f2boptions = json.loads(r.get('F2B_OPTIONS'))
|
f2boptions = json.loads(r.get('F2B_OPTIONS'))
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
logger.logCrit('Error loading F2B options: F2B_OPTIONS is not json')
|
logger.logCrit(
|
||||||
|
'Error loading F2B options: F2B_OPTIONS is not json. Exception: %s' % e)
|
||||||
quit_now = True
|
quit_now = True
|
||||||
exit_code = 2
|
exit_code = 2
|
||||||
|
|
||||||
@@ -61,15 +65,15 @@ def refreshF2boptions():
|
|||||||
r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
|
r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
|
||||||
|
|
||||||
def verifyF2boptions(f2boptions):
|
def verifyF2boptions(f2boptions):
|
||||||
verifyF2boption(f2boptions,'ban_time', 1800)
|
verifyF2boption(f2boptions, 'ban_time', 1800)
|
||||||
verifyF2boption(f2boptions,'max_ban_time', 10000)
|
verifyF2boption(f2boptions, 'max_ban_time', 10000)
|
||||||
verifyF2boption(f2boptions,'ban_time_increment', True)
|
verifyF2boption(f2boptions, 'ban_time_increment', True)
|
||||||
verifyF2boption(f2boptions,'max_attempts', 10)
|
verifyF2boption(f2boptions, 'max_attempts', 10)
|
||||||
verifyF2boption(f2boptions,'retry_window', 600)
|
verifyF2boption(f2boptions, 'retry_window', 600)
|
||||||
verifyF2boption(f2boptions,'netban_ipv4', 32)
|
verifyF2boption(f2boptions, 'netban_ipv4', 32)
|
||||||
verifyF2boption(f2boptions,'netban_ipv6', 128)
|
verifyF2boption(f2boptions, 'netban_ipv6', 128)
|
||||||
verifyF2boption(f2boptions,'banlist_id', str(uuid.uuid4()))
|
verifyF2boption(f2boptions, 'banlist_id', str(uuid.uuid4()))
|
||||||
verifyF2boption(f2boptions,'manage_external', 0)
|
verifyF2boption(f2boptions, 'manage_external', 0)
|
||||||
|
|
||||||
def verifyF2boption(f2boptions, f2boption, f2bdefault):
|
def verifyF2boption(f2boptions, f2boption, f2bdefault):
|
||||||
f2boptions[f2boption] = f2boptions[f2boption] if f2boption in f2boptions and f2boptions[f2boption] is not None else f2bdefault
|
f2boptions[f2boption] = f2boptions[f2boption] if f2boption in f2boptions and f2boptions[f2boption] is not None else f2bdefault
|
||||||
@@ -111,7 +115,7 @@ def get_ip(address):
|
|||||||
def ban(address):
|
def ban(address):
|
||||||
global f2boptions
|
global f2boptions
|
||||||
global lock
|
global lock
|
||||||
|
logdebug("ban() called with address=%s" % address)
|
||||||
refreshF2boptions()
|
refreshF2boptions()
|
||||||
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
|
||||||
RETRY_WINDOW = int(f2boptions['retry_window'])
|
RETRY_WINDOW = int(f2boptions['retry_window'])
|
||||||
@@ -119,31 +123,43 @@ def ban(address):
|
|||||||
NETBAN_IPV6 = '/' + str(f2boptions['netban_ipv6'])
|
NETBAN_IPV6 = '/' + str(f2boptions['netban_ipv6'])
|
||||||
|
|
||||||
ip = get_ip(address)
|
ip = get_ip(address)
|
||||||
if not ip: return
|
if not ip:
|
||||||
|
logdebug("No valid IP -- skipping ban()")
|
||||||
|
return
|
||||||
address = str(ip)
|
address = str(ip)
|
||||||
self_network = ipaddress.ip_network(address)
|
self_network = ipaddress.ip_network(address)
|
||||||
|
|
||||||
with lock:
|
with lock:
|
||||||
temp_whitelist = set(WHITELIST)
|
temp_whitelist = set(WHITELIST)
|
||||||
if temp_whitelist:
|
logdebug("Checking if %s overlaps with any WHITELIST entries" % self_network)
|
||||||
for wl_key in temp_whitelist:
|
if temp_whitelist:
|
||||||
wl_net = ipaddress.ip_network(wl_key, False)
|
for wl_key in temp_whitelist:
|
||||||
if wl_net.overlaps(self_network):
|
wl_net = ipaddress.ip_network(wl_key, False)
|
||||||
logger.logInfo('Address %s is whitelisted by rule %s' % (self_network, wl_net))
|
logdebug("Checking overlap between %s and %s" % (self_network, wl_net))
|
||||||
return
|
if wl_net.overlaps(self_network):
|
||||||
|
logger.logInfo(
|
||||||
|
'Address %s is allowlisted by rule %s' % (self_network, wl_net))
|
||||||
|
return
|
||||||
|
|
||||||
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)
|
||||||
|
logdebug("Ban net: %s" % net)
|
||||||
|
|
||||||
if not net in bans:
|
if not net in bans:
|
||||||
bans[net] = {'attempts': 0, 'last_attempt': 0, 'ban_counter': 0}
|
bans[net] = {'attempts': 0, 'last_attempt': 0, 'ban_counter': 0}
|
||||||
|
logdebug("Initing new ban counter for %s" % net)
|
||||||
|
|
||||||
current_attempt = time.time()
|
current_attempt = time.time()
|
||||||
|
logdebug("Current attempt ts=%s, previous: %s, retry_window: %s" %
|
||||||
|
(current_attempt, bans[net]['last_attempt'], RETRY_WINDOW))
|
||||||
if current_attempt - bans[net]['last_attempt'] > RETRY_WINDOW:
|
if current_attempt - bans[net]['last_attempt'] > RETRY_WINDOW:
|
||||||
bans[net]['attempts'] = 0
|
bans[net]['attempts'] = 0
|
||||||
|
logdebug("Ban counter for %s reset as window expired" % net)
|
||||||
|
|
||||||
bans[net]['attempts'] += 1
|
bans[net]['attempts'] += 1
|
||||||
bans[net]['last_attempt'] = current_attempt
|
bans[net]['last_attempt'] = current_attempt
|
||||||
|
logdebug("%s attempts now %d" % (net, bans[net]['attempts']))
|
||||||
|
|
||||||
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
||||||
cur_time = int(round(time.time()))
|
cur_time = int(round(time.time()))
|
||||||
@@ -151,34 +167,41 @@ def ban(address):
|
|||||||
logger.logCrit('Banning %s for %d minutes' % (net, NET_BAN_TIME / 60 ))
|
logger.logCrit('Banning %s for %d minutes' % (net, NET_BAN_TIME / 60 ))
|
||||||
if type(ip) is ipaddress.IPv4Address and int(f2boptions['manage_external']) != 1:
|
if type(ip) is ipaddress.IPv4Address and int(f2boptions['manage_external']) != 1:
|
||||||
with lock:
|
with lock:
|
||||||
|
logdebug("Calling tables.banIPv4(%s)" % net)
|
||||||
tables.banIPv4(net)
|
tables.banIPv4(net)
|
||||||
elif int(f2boptions['manage_external']) != 1:
|
elif int(f2boptions['manage_external']) != 1:
|
||||||
with lock:
|
with lock:
|
||||||
|
logdebug("Calling tables.banIPv6(%s)" % net)
|
||||||
tables.banIPv6(net)
|
tables.banIPv6(net)
|
||||||
|
|
||||||
|
logdebug("Updating F2B_ACTIVE_BANS[%s]=%d" %
|
||||||
|
(net, cur_time + NET_BAN_TIME))
|
||||||
r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + NET_BAN_TIME)
|
r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + NET_BAN_TIME)
|
||||||
else:
|
else:
|
||||||
logger.logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
|
logger.logWarn('%d more attempts in the next %d seconds until %s is banned' % (
|
||||||
|
MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
|
||||||
|
|
||||||
def unban(net):
|
def unban(net):
|
||||||
global lock
|
global lock
|
||||||
|
logdebug("Calling unban() with net=%s" % net)
|
||||||
if not net in bans:
|
if not net in bans:
|
||||||
logger.logInfo('%s is not banned, skipping unban and deleting from queue (if any)' % net)
|
logger.logInfo(
|
||||||
r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
|
'%s is not banned, skipping unban and deleting from queue (if any)' % net)
|
||||||
return
|
r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
|
||||||
|
return
|
||||||
logger.logInfo('Unbanning %s' % net)
|
logger.logInfo('Unbanning %s' % net)
|
||||||
if type(ipaddress.ip_network(net)) is ipaddress.IPv4Network:
|
if type(ipaddress.ip_network(net)) is ipaddress.IPv4Network:
|
||||||
with lock:
|
with lock:
|
||||||
|
logdebug("Calling tables.unbanIPv4(%s)" % net)
|
||||||
tables.unbanIPv4(net)
|
tables.unbanIPv4(net)
|
||||||
else:
|
else:
|
||||||
with lock:
|
with lock:
|
||||||
|
logdebug("Calling tables.unbanIPv6(%s)" % net)
|
||||||
tables.unbanIPv6(net)
|
tables.unbanIPv6(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:
|
||||||
|
logdebug("Unban for %s, setting attempts=0, ban_counter+=1" % net)
|
||||||
bans[net]['attempts'] = 0
|
bans[net]['attempts'] = 0
|
||||||
bans[net]['ban_counter'] += 1
|
bans[net]['ban_counter'] += 1
|
||||||
|
|
||||||
@@ -204,17 +227,19 @@ def permBan(net, unban=False):
|
|||||||
|
|
||||||
if is_unbanned:
|
if is_unbanned:
|
||||||
r.hdel('F2B_PERM_BANS', '%s' % net)
|
r.hdel('F2B_PERM_BANS', '%s' % net)
|
||||||
logger.logCrit('Removed host/network %s from blacklist' % net)
|
logger.logCrit('Removed host/network %s from denylist' % net)
|
||||||
elif is_banned:
|
elif is_banned:
|
||||||
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
||||||
logger.logCrit('Added host/network %s to blacklist' % net)
|
logger.logCrit('Added host/network %s to denylist' % net)
|
||||||
|
|
||||||
def clear():
|
def clear():
|
||||||
global lock
|
global lock
|
||||||
logger.logInfo('Clearing all bans')
|
logger.logInfo('Clearing all bans')
|
||||||
for net in bans.copy():
|
for net in bans.copy():
|
||||||
|
logdebug("Unbanning net: %s" % net)
|
||||||
unban(net)
|
unban(net)
|
||||||
with lock:
|
with lock:
|
||||||
|
logdebug("Clearing IPv4/IPv6 table")
|
||||||
tables.clearIPv4Table()
|
tables.clearIPv4Table()
|
||||||
tables.clearIPv6Table()
|
tables.clearIPv6Table()
|
||||||
try:
|
try:
|
||||||
@@ -275,21 +300,35 @@ def snat6(snat_target):
|
|||||||
|
|
||||||
def autopurge():
|
def autopurge():
|
||||||
global f2boptions
|
global f2boptions
|
||||||
|
logdebug("autopurge thread started")
|
||||||
while not quit_now:
|
while not quit_now:
|
||||||
|
logdebug("autopurge tick")
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
refreshF2boptions()
|
refreshF2boptions()
|
||||||
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')
|
||||||
|
logdebug("QUEUE_UNBAN: %s" % QUEUE_UNBAN)
|
||||||
if QUEUE_UNBAN:
|
if QUEUE_UNBAN:
|
||||||
for net in QUEUE_UNBAN:
|
for net in QUEUE_UNBAN:
|
||||||
|
logdebug("Autopurge: unbanning queued net: %s" % net)
|
||||||
unban(str(net))
|
unban(str(net))
|
||||||
for net in bans.copy():
|
# Only check expiry for actively banned IPs:
|
||||||
if bans[net]['attempts'] >= MAX_ATTEMPTS:
|
active_bans = r.hgetall('F2B_ACTIVE_BANS')
|
||||||
NET_BAN_TIME = calcNetBanTime(bans[net]['ban_counter'])
|
now = time.time()
|
||||||
TIME_SINCE_LAST_ATTEMPT = time.time() - bans[net]['last_attempt']
|
for net_str, expire_str in active_bans.items():
|
||||||
if TIME_SINCE_LAST_ATTEMPT > NET_BAN_TIME:
|
logdebug("Checking ban expiry for (actively banned): %s" % net_str)
|
||||||
unban(net)
|
# Defensive: always process if timer missing or expired
|
||||||
|
try:
|
||||||
|
expire = float(expire_str)
|
||||||
|
except Exception:
|
||||||
|
logdebug("Invalid expire time for %s; unbanning" % net_str)
|
||||||
|
unban(net_str)
|
||||||
|
continue
|
||||||
|
time_left = expire - now
|
||||||
|
logdebug("Time left for %s: %.1f seconds" % (net_str, time_left))
|
||||||
|
if time_left <= 0:
|
||||||
|
logdebug("Ban expired for %s" % net_str)
|
||||||
|
unban(net_str)
|
||||||
|
|
||||||
def mailcowChainOrder():
|
def mailcowChainOrder():
|
||||||
global lock
|
global lock
|
||||||
@@ -359,7 +398,7 @@ def whitelistUpdate():
|
|||||||
with lock:
|
with lock:
|
||||||
if Counter(new_whitelist) != Counter(WHITELIST):
|
if Counter(new_whitelist) != Counter(WHITELIST):
|
||||||
WHITELIST = new_whitelist
|
WHITELIST = new_whitelist
|
||||||
logger.logInfo('Whitelist was changed, it has %s entries' % len(WHITELIST))
|
logger.logInfo('Allowlist was changed, it has %s entries' % len(WHITELIST))
|
||||||
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
||||||
|
|
||||||
def blacklistUpdate():
|
def blacklistUpdate():
|
||||||
@@ -375,7 +414,7 @@ def blacklistUpdate():
|
|||||||
addban = set(new_blacklist).difference(BLACKLIST)
|
addban = set(new_blacklist).difference(BLACKLIST)
|
||||||
delban = set(BLACKLIST).difference(new_blacklist)
|
delban = set(BLACKLIST).difference(new_blacklist)
|
||||||
BLACKLIST = new_blacklist
|
BLACKLIST = new_blacklist
|
||||||
logger.logInfo('Blacklist was changed, it has %s entries' % len(BLACKLIST))
|
logger.logInfo('Denylist was changed, it has %s entries' % len(BLACKLIST))
|
||||||
if addban:
|
if addban:
|
||||||
for net in addban:
|
for net in addban:
|
||||||
permBan(net=net)
|
permBan(net=net)
|
||||||
@@ -386,42 +425,43 @@ def blacklistUpdate():
|
|||||||
|
|
||||||
def sigterm_quit(signum, frame):
|
def sigterm_quit(signum, frame):
|
||||||
global clear_before_quit
|
global clear_before_quit
|
||||||
|
logdebug("SIGTERM received, setting clear_before_quit to True and exiting")
|
||||||
clear_before_quit = True
|
clear_before_quit = True
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
def berfore_quit():
|
def before_quit():
|
||||||
|
logdebug("before_quit called, clear_before_quit=%s" % clear_before_quit)
|
||||||
if clear_before_quit:
|
if clear_before_quit:
|
||||||
clear()
|
clear()
|
||||||
if pubsub is not None:
|
if pubsub is not None:
|
||||||
pubsub.unsubscribe()
|
pubsub.unsubscribe()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
atexit.register(berfore_quit)
|
logger = Logger()
|
||||||
|
logdebug("Sys.argv: %s" % sys.argv)
|
||||||
|
atexit.register(before_quit)
|
||||||
signal.signal(signal.SIGTERM, sigterm_quit)
|
signal.signal(signal.SIGTERM, sigterm_quit)
|
||||||
|
|
||||||
# init Logger
|
|
||||||
logger = Logger()
|
|
||||||
|
|
||||||
# init backend
|
|
||||||
backend = sys.argv[1]
|
backend = sys.argv[1]
|
||||||
|
logdebug("Backend: %s" % backend)
|
||||||
if backend == "nftables":
|
if backend == "nftables":
|
||||||
logger.logInfo('Using NFTables backend')
|
logger.logInfo('Using NFTables backend')
|
||||||
tables = NFTables(chain_name, logger)
|
tables = NFTables(chain_name, logger)
|
||||||
else:
|
else:
|
||||||
logger.logInfo('Using IPTables backend')
|
logger.logInfo('Using IPTables backend')
|
||||||
|
logger.logWarn(
|
||||||
|
"DEPRECATION: iptables-legacy is deprecated and will be removed in future releases. "
|
||||||
|
"Please switch to nftables on your host to ensure complete compatibility."
|
||||||
|
)
|
||||||
|
time.sleep(5)
|
||||||
tables = IPTables(chain_name, logger)
|
tables = IPTables(chain_name, logger)
|
||||||
|
|
||||||
# In case a previous session was killed without cleanup
|
|
||||||
clear()
|
clear()
|
||||||
|
|
||||||
# Reinit MAILCOW chain
|
|
||||||
# Is called before threads start, no locking
|
|
||||||
logger.logInfo("Initializing mailcow netfilter chain")
|
logger.logInfo("Initializing mailcow netfilter chain")
|
||||||
tables.initChainIPv4()
|
tables.initChainIPv4()
|
||||||
tables.initChainIPv6()
|
tables.initChainIPv6()
|
||||||
|
|
||||||
if os.getenv("DISABLE_NETFILTER_ISOLATION_RULE").lower() in ("y", "yes"):
|
if os.getenv("DISABLE_NETFILTER_ISOLATION_RULE", "").lower() in ("y", "yes"):
|
||||||
logger.logInfo(f"Skipping {chain_name} isolation")
|
logger.logInfo(f"Skipping {chain_name} isolation")
|
||||||
else:
|
else:
|
||||||
logger.logInfo(f"Setting {chain_name} isolation")
|
logger.logInfo(f"Setting {chain_name} isolation")
|
||||||
@@ -432,23 +472,28 @@ if __name__ == '__main__':
|
|||||||
try:
|
try:
|
||||||
redis_slaveof_ip = os.getenv('REDIS_SLAVEOF_IP', '')
|
redis_slaveof_ip = os.getenv('REDIS_SLAVEOF_IP', '')
|
||||||
redis_slaveof_port = os.getenv('REDIS_SLAVEOF_PORT', '')
|
redis_slaveof_port = os.getenv('REDIS_SLAVEOF_PORT', '')
|
||||||
|
logdebug(
|
||||||
|
"Connecting redis (SLAVEOF_IP:%s, PORT:%s)" % (redis_slaveof_ip, redis_slaveof_port))
|
||||||
if "".__eq__(redis_slaveof_ip):
|
if "".__eq__(redis_slaveof_ip):
|
||||||
r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0, password=os.environ['REDISPASS'])
|
r = redis.StrictRedis(
|
||||||
|
host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0, password=os.environ['REDISPASS'])
|
||||||
else:
|
else:
|
||||||
r = redis.StrictRedis(host=redis_slaveof_ip, decode_responses=True, port=redis_slaveof_port, db=0, password=os.environ['REDISPASS'])
|
r = redis.StrictRedis(
|
||||||
|
host=redis_slaveof_ip, decode_responses=True, port=redis_slaveof_port, db=0, password=os.environ['REDISPASS'])
|
||||||
r.ping()
|
r.ping()
|
||||||
pubsub = r.pubsub()
|
pubsub = r.pubsub()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print('%s - trying again in 3 seconds' % (ex))
|
logdebug(
|
||||||
|
'Redis connection failed: %s - trying again in 3 seconds' % (ex))
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
logger.set_redis(r)
|
logger.set_redis(r)
|
||||||
|
logdebug("Redis connection established, setting up F2B keys")
|
||||||
|
|
||||||
# rename fail2ban to netfilter
|
|
||||||
if r.exists('F2B_LOG'):
|
if r.exists('F2B_LOG'):
|
||||||
|
logdebug("Renaming F2B_LOG to NETFILTER_LOG")
|
||||||
r.rename('F2B_LOG', 'NETFILTER_LOG')
|
r.rename('F2B_LOG', 'NETFILTER_LOG')
|
||||||
# clear bans in redis
|
|
||||||
r.delete('F2B_ACTIVE_BANS')
|
r.delete('F2B_ACTIVE_BANS')
|
||||||
r.delete('F2B_PERM_BANS')
|
r.delete('F2B_PERM_BANS')
|
||||||
|
|
||||||
@@ -463,7 +508,7 @@ if __name__ == '__main__':
|
|||||||
snat_ip = os.getenv('SNAT_TO_SOURCE')
|
snat_ip = os.getenv('SNAT_TO_SOURCE')
|
||||||
snat_ipo = ipaddress.ip_address(snat_ip)
|
snat_ipo = ipaddress.ip_address(snat_ip)
|
||||||
if type(snat_ipo) is ipaddress.IPv4Address:
|
if type(snat_ipo) is ipaddress.IPv4Address:
|
||||||
snat4_thread = Thread(target=snat4,args=(snat_ip,))
|
snat4_thread = Thread(target=snat4, args=(snat_ip,))
|
||||||
snat4_thread.daemon = True
|
snat4_thread.daemon = True
|
||||||
snat4_thread.start()
|
snat4_thread.start()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@@ -499,4 +544,5 @@ if __name__ == '__main__':
|
|||||||
while not quit_now:
|
while not quit_now:
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
sys.exit(exit_code)
|
logdebug("Exiting with code %s" % exit_code)
|
||||||
|
sys.exit(exit_code)
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
|
import datetime
|
||||||
|
|
||||||
class Logger:
|
class Logger:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -8,17 +9,28 @@ class Logger:
|
|||||||
def set_redis(self, redis):
|
def set_redis(self, redis):
|
||||||
self.r = redis
|
self.r = redis
|
||||||
|
|
||||||
|
def _format_timestamp(self):
|
||||||
|
# Local time with milliseconds
|
||||||
|
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
def log(self, priority, message):
|
def log(self, priority, message):
|
||||||
tolog = {}
|
# build redis-friendly dict
|
||||||
tolog['time'] = int(round(time.time()))
|
tolog = {
|
||||||
tolog['priority'] = priority
|
'time': int(round(time.time())), # keep raw timestamp for Redis
|
||||||
tolog['message'] = message
|
'priority': priority,
|
||||||
print(message)
|
'message': message
|
||||||
|
}
|
||||||
|
|
||||||
|
# print human-readable message with timestamp
|
||||||
|
ts = self._format_timestamp()
|
||||||
|
print(f"{ts} {priority.upper()}: {message}", flush=True)
|
||||||
|
|
||||||
|
# also push JSON to Redis if connected
|
||||||
if self.r is not None:
|
if self.r is not None:
|
||||||
try:
|
try:
|
||||||
self.r.lpush('NETFILTER_LOG', json.dumps(tolog, ensure_ascii=False))
|
self.r.lpush('NETFILTER_LOG', json.dumps(tolog, ensure_ascii=False))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print('Failed logging to redis: %s' % (ex))
|
print(f'{ts} WARN: Failed logging to redis: {ex}', flush=True)
|
||||||
|
|
||||||
def logWarn(self, message):
|
def logWarn(self, message):
|
||||||
self.log('warn', message)
|
self.log('warn', message)
|
||||||
@@ -27,4 +39,4 @@ class Logger:
|
|||||||
self.log('crit', message)
|
self.log('crit', message)
|
||||||
|
|
||||||
def logInfo(self, message):
|
def logInfo(self, message):
|
||||||
self.log('info', message)
|
self.log('info', message)
|
||||||
@@ -10,7 +10,7 @@ def includes_conf(env, template_vars):
|
|||||||
server_name_config = f"server_name {template_vars['MAILCOW_HOSTNAME']} autodiscover.* autoconfig.* {' '.join(template_vars['ADDITIONAL_SERVER_NAMES'])};"
|
server_name_config = f"server_name {template_vars['MAILCOW_HOSTNAME']} autodiscover.* autoconfig.* {' '.join(template_vars['ADDITIONAL_SERVER_NAMES'])};"
|
||||||
listen_plain_config = f"listen {template_vars['HTTP_PORT']};"
|
listen_plain_config = f"listen {template_vars['HTTP_PORT']};"
|
||||||
listen_ssl_config = f"listen {template_vars['HTTPS_PORT']};"
|
listen_ssl_config = f"listen {template_vars['HTTPS_PORT']};"
|
||||||
if not template_vars['DISABLE_IPv6']:
|
if template_vars['ENABLE_IPV6']:
|
||||||
listen_plain_config += f"\nlisten [::]:{template_vars['HTTP_PORT']};"
|
listen_plain_config += f"\nlisten [::]:{template_vars['HTTP_PORT']};"
|
||||||
listen_ssl_config += f"\nlisten [::]:{template_vars['HTTPS_PORT']} ssl;"
|
listen_ssl_config += f"\nlisten [::]:{template_vars['HTTPS_PORT']} ssl;"
|
||||||
listen_ssl_config += "\nhttp2 on;"
|
listen_ssl_config += "\nhttp2 on;"
|
||||||
@@ -58,7 +58,7 @@ def prepare_template_vars():
|
|||||||
'SOGOHOST': os.getenv("SOGOHOST", ipv4_network + ".248"),
|
'SOGOHOST': os.getenv("SOGOHOST", ipv4_network + ".248"),
|
||||||
'RSPAMDHOST': os.getenv("RSPAMDHOST", "rspamd-mailcow"),
|
'RSPAMDHOST': os.getenv("RSPAMDHOST", "rspamd-mailcow"),
|
||||||
'PHPFPMHOST': os.getenv("PHPFPMHOST", "php-fpm-mailcow"),
|
'PHPFPMHOST': os.getenv("PHPFPMHOST", "php-fpm-mailcow"),
|
||||||
'DISABLE_IPv6': os.getenv("DISABLE_IPv6", "n").lower() in ("y", "yes"),
|
'ENABLE_IPV6': os.getenv("ENABLE_IPV6", "true").lower() != "false",
|
||||||
'HTTP_REDIRECT': os.getenv("HTTP_REDIRECT", "n").lower() in ("y", "yes"),
|
'HTTP_REDIRECT': os.getenv("HTTP_REDIRECT", "n").lower() in ("y", "yes"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,13 @@ import time
|
|||||||
import magic
|
import magic
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
skip_olefy = os.getenv('SKIP_OLEFY', '')
|
||||||
|
|
||||||
|
if skip_olefy.lower() in ['yes', 'y']:
|
||||||
|
print("SKIP_OLEFY=y, skipping Olefy...")
|
||||||
|
time.sleep(365 * 24 * 60 * 60)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
# merge variables from /etc/olefy.conf and the defaults
|
# merge variables from /etc/olefy.conf and the defaults
|
||||||
olefy_listen_addr_string = os.getenv('OLEFY_BINDADDRESS', '127.0.0.1,::1')
|
olefy_listen_addr_string = os.getenv('OLEFY_BINDADDRESS', '127.0.0.1,::1')
|
||||||
olefy_listen_port = int(os.getenv('OLEFY_BINDPORT', '10050'))
|
olefy_listen_port = int(os.getenv('OLEFY_BINDPORT', '10050'))
|
||||||
@@ -113,7 +120,7 @@ def oletools( stream, tmp_file_name, lid ):
|
|||||||
out = bytes(out.decode('utf-8', 'ignore').replace(' ', ' ').replace('\t', '').replace('\n', '').replace('XLMMacroDeobfuscator: pywin32 is not installed (only is required if you want to use MS Excel)', ''), encoding="utf-8")
|
out = bytes(out.decode('utf-8', 'ignore').replace(' ', ' ').replace('\t', '').replace('\n', '').replace('XLMMacroDeobfuscator: pywin32 is not installed (only is required if you want to use MS Excel)', ''), encoding="utf-8")
|
||||||
failed = False
|
failed = False
|
||||||
if out.__len__() < 30:
|
if out.__len__() < 30:
|
||||||
logger.error('{} olevba returned <30 chars - rc: {!r}, response: {!r}, error: {!r}'.format(lid,cmd_tmp.returncode,
|
logger.error('{} olevba returned <30 chars - rc: {!r}, response: {!r}, error: {!r}'.format(lid,cmd_tmp.returncode,
|
||||||
out.decode('utf-8', 'ignore'), err.decode('utf-8', 'ignore')))
|
out.decode('utf-8', 'ignore'), err.decode('utf-8', 'ignore')))
|
||||||
out = b'[ { "error": "Unhandled error - too short olevba response" } ]'
|
out = b'[ { "error": "Unhandled error - too short olevba response" } ]'
|
||||||
failed = True
|
failed = True
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ FROM php:8.2-fpm-alpine3.21
|
|||||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
||||||
|
|
||||||
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||||
ARG APCU_PECL_VERSION=5.1.24
|
ARG APCU_PECL_VERSION=5.1.27
|
||||||
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
|
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||||
ARG IMAGICK_PECL_VERSION=3.7.0
|
ARG IMAGICK_PECL_VERSION=3.8.0
|
||||||
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||||
ARG MAILPARSE_PECL_VERSION=3.1.8
|
ARG MAILPARSE_PECL_VERSION=3.1.9
|
||||||
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||||
ARG MEMCACHED_PECL_VERSION=3.2.0
|
ARG MEMCACHED_PECL_VERSION=3.3.0
|
||||||
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
|
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||||
ARG REDIS_PECL_VERSION=6.1.0
|
ARG REDIS_PECL_VERSION=6.2.0
|
||||||
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
|
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||||
ARG COMPOSER_VERSION=2.8.6
|
ARG COMPOSER_VERSION=2.8.6
|
||||||
|
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ DELIMITER //
|
|||||||
CREATE EVENT clean_spamalias
|
CREATE EVENT clean_spamalias
|
||||||
ON SCHEDULE EVERY 1 DAY DO
|
ON SCHEDULE EVERY 1 DAY DO
|
||||||
BEGIN
|
BEGIN
|
||||||
DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP();
|
DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP() AND permanent = 0;
|
||||||
END;
|
END;
|
||||||
//
|
//
|
||||||
DELIMITER ;
|
DELIMITER ;
|
||||||
|
|||||||
50
data/Dockerfiles/postfix-tlspol/Dockerfile
Normal file
50
data/Dockerfiles/postfix-tlspol/Dockerfile
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
FROM golang:1.25-bookworm AS builder
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
ENV CGO_ENABLED=0 \
|
||||||
|
GO111MODULE=on \
|
||||||
|
NOOPT=1 \
|
||||||
|
VERSION=1.8.22
|
||||||
|
|
||||||
|
RUN git clone --branch v${VERSION} https://github.com/Zuplu/postfix-tlspol && \
|
||||||
|
cd /src/postfix-tlspol && \
|
||||||
|
scripts/build.sh build-only
|
||||||
|
|
||||||
|
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||||
|
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
ENV LC_ALL=C
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
dirmngr \
|
||||||
|
dnsutils \
|
||||||
|
iputils-ping \
|
||||||
|
sudo \
|
||||||
|
supervisor \
|
||||||
|
redis-tools \
|
||||||
|
syslog-ng \
|
||||||
|
syslog-ng-core \
|
||||||
|
syslog-ng-mod-redis \
|
||||||
|
tzdata \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& touch /etc/default/locale
|
||||||
|
|
||||||
|
COPY supervisord.conf /etc/supervisor/supervisord.conf
|
||||||
|
COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf
|
||||||
|
COPY syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng-redis_slave.conf
|
||||||
|
COPY postfix-tlspol.sh /opt/postfix-tlspol.sh
|
||||||
|
COPY stop-supervisor.sh /usr/local/sbin/stop-supervisor.sh
|
||||||
|
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
COPY --from=builder /src/postfix-tlspol/build/postfix-tlspol /usr/local/bin/postfix-tlspol
|
||||||
|
|
||||||
|
RUN chmod +x /opt/postfix-tlspol.sh \
|
||||||
|
/usr/local/sbin/stop-supervisor.sh \
|
||||||
|
/docker-entrypoint.sh
|
||||||
|
RUN rm -rf /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
|
|
||||||
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
|
||||||
7
data/Dockerfiles/postfix-tlspol/docker-entrypoint.sh
Executable file
7
data/Dockerfiles/postfix-tlspol/docker-entrypoint.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
|
||||||
|
cp /etc/syslog-ng/syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
||||||
52
data/Dockerfiles/postfix-tlspol/postfix-tlspol.sh
Executable file
52
data/Dockerfiles/postfix-tlspol/postfix-tlspol.sh
Executable file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
LOGLVL=info
|
||||||
|
|
||||||
|
if [ ${DEV_MODE} != "n" ]; then
|
||||||
|
echo -e "\e[31mEnabling debug mode\e[0m"
|
||||||
|
set -x
|
||||||
|
LOGLVL=debug
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ ! -d /etc/postfix-tlspol ]] && mkdir -p /etc/postfix-tlspol
|
||||||
|
[[ ! -d /var/lib/postfix-tlspol ]] && mkdir -p /var/lib/postfix-tlspol
|
||||||
|
|
||||||
|
until dig +short mailcow.email > /dev/null; do
|
||||||
|
echo "Waiting for DNS..."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Do not attempt to write to slave
|
||||||
|
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
|
||||||
|
export REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning"
|
||||||
|
else
|
||||||
|
export REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning"
|
||||||
|
fi
|
||||||
|
|
||||||
|
until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do
|
||||||
|
echo "Waiting for Redis..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Waiting for Postfix..."
|
||||||
|
until ping postfix -c1 > /dev/null; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "Postfix OK"
|
||||||
|
|
||||||
|
cat <<EOF > /etc/postfix-tlspol/config.yaml
|
||||||
|
server:
|
||||||
|
address: 0.0.0.0:8642
|
||||||
|
|
||||||
|
log-level: ${LOGLVL}
|
||||||
|
|
||||||
|
prefetch: true
|
||||||
|
|
||||||
|
cache-file: /var/lib/postfix-tlspol/cache.db
|
||||||
|
|
||||||
|
dns:
|
||||||
|
# must support DNSSEC
|
||||||
|
address: 127.0.0.11:53
|
||||||
|
EOF
|
||||||
|
|
||||||
|
/usr/local/bin/postfix-tlspol -config /etc/postfix-tlspol/config.yaml
|
||||||
8
data/Dockerfiles/postfix-tlspol/stop-supervisor.sh
Executable file
8
data/Dockerfiles/postfix-tlspol/stop-supervisor.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
printf "READY\n";
|
||||||
|
|
||||||
|
while read line; do
|
||||||
|
echo "Processing Event: $line" >&2;
|
||||||
|
kill -3 $(cat "/var/run/supervisord.pid")
|
||||||
|
done < /dev/stdin
|
||||||
25
data/Dockerfiles/postfix-tlspol/supervisord.conf
Normal file
25
data/Dockerfiles/postfix-tlspol/supervisord.conf
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[supervisord]
|
||||||
|
pidfile=/var/run/supervisord.pid
|
||||||
|
nodaemon=true
|
||||||
|
user=root
|
||||||
|
|
||||||
|
[program:syslog-ng]
|
||||||
|
command=/usr/sbin/syslog-ng --foreground --no-caps
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
autostart=true
|
||||||
|
|
||||||
|
[program:postfix-tlspol]
|
||||||
|
startsecs=10
|
||||||
|
autorestart=true
|
||||||
|
command=/opt/postfix-tlspol.sh
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
|
||||||
|
[eventlistener:processes]
|
||||||
|
command=/usr/local/sbin/stop-supervisor.sh
|
||||||
|
events=PROCESS_STATE_STOPPED, PROCESS_STATE_EXITED, PROCESS_STATE_FATAL
|
||||||
45
data/Dockerfiles/postfix-tlspol/syslog-ng-redis_slave.conf
Normal file
45
data/Dockerfiles/postfix-tlspol/syslog-ng-redis_slave.conf
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
@version: 3.38
|
||||||
|
@include "scl.conf"
|
||||||
|
options {
|
||||||
|
chain_hostnames(off);
|
||||||
|
flush_lines(0);
|
||||||
|
use_dns(no);
|
||||||
|
dns_cache(no);
|
||||||
|
use_fqdn(no);
|
||||||
|
owner("root"); group("adm"); perm(0640);
|
||||||
|
stats_freq(0);
|
||||||
|
bad_hostname("^gconfd$");
|
||||||
|
};
|
||||||
|
source s_src {
|
||||||
|
unix-stream("/dev/log");
|
||||||
|
internal();
|
||||||
|
};
|
||||||
|
destination d_stdout { pipe("/dev/stdout"); };
|
||||||
|
destination d_redis_ui_log {
|
||||||
|
redis(
|
||||||
|
host("`REDIS_SLAVEOF_IP`")
|
||||||
|
persist-name("redis1")
|
||||||
|
port(`REDIS_SLAVEOF_PORT`)
|
||||||
|
auth("`REDISPASS`")
|
||||||
|
command("LPUSH" "POSTFIX_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
filter f_mail { facility(mail); };
|
||||||
|
# start
|
||||||
|
# overriding warnings are still displayed when the entrypoint runs its initial check
|
||||||
|
# warnings logged by postfix-mailcow to syslog are hidden to reduce repeating msgs
|
||||||
|
# Some other warnings are ignored
|
||||||
|
filter f_ignore {
|
||||||
|
not match("overriding earlier entry" value("MESSAGE"));
|
||||||
|
not match("TLS SNI from checks.mailcow.email" value("MESSAGE"));
|
||||||
|
not match("no SASL support" value("MESSAGE"));
|
||||||
|
not facility (local0, local1, local2, local3, local4, local5, local6, local7);
|
||||||
|
};
|
||||||
|
# end
|
||||||
|
log {
|
||||||
|
source(s_src);
|
||||||
|
filter(f_ignore);
|
||||||
|
destination(d_stdout);
|
||||||
|
filter(f_mail);
|
||||||
|
destination(d_redis_ui_log);
|
||||||
|
};
|
||||||
45
data/Dockerfiles/postfix-tlspol/syslog-ng.conf
Normal file
45
data/Dockerfiles/postfix-tlspol/syslog-ng.conf
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
@version: 3.38
|
||||||
|
@include "scl.conf"
|
||||||
|
options {
|
||||||
|
chain_hostnames(off);
|
||||||
|
flush_lines(0);
|
||||||
|
use_dns(no);
|
||||||
|
dns_cache(no);
|
||||||
|
use_fqdn(no);
|
||||||
|
owner("root"); group("adm"); perm(0640);
|
||||||
|
stats_freq(0);
|
||||||
|
bad_hostname("^gconfd$");
|
||||||
|
};
|
||||||
|
source s_src {
|
||||||
|
unix-stream("/dev/log");
|
||||||
|
internal();
|
||||||
|
};
|
||||||
|
destination d_stdout { pipe("/dev/stdout"); };
|
||||||
|
destination d_redis_ui_log {
|
||||||
|
redis(
|
||||||
|
host("redis-mailcow")
|
||||||
|
persist-name("redis1")
|
||||||
|
port(6379)
|
||||||
|
auth("`REDISPASS`")
|
||||||
|
command("LPUSH" "POSTFIX_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
filter f_mail { facility(mail); };
|
||||||
|
# start
|
||||||
|
# overriding warnings are still displayed when the entrypoint runs its initial check
|
||||||
|
# warnings logged by postfix-mailcow to syslog are hidden to reduce repeating msgs
|
||||||
|
# Some other warnings are ignored
|
||||||
|
filter f_ignore {
|
||||||
|
not match("overriding earlier entry" value("MESSAGE"));
|
||||||
|
not match("TLS SNI from checks.mailcow.email" value("MESSAGE"));
|
||||||
|
not match("no SASL support" value("MESSAGE"));
|
||||||
|
not facility (local0, local1, local2, local3, local4, local5, local6, local7);
|
||||||
|
};
|
||||||
|
# end
|
||||||
|
log {
|
||||||
|
source(s_src);
|
||||||
|
filter(f_ignore);
|
||||||
|
destination(d_stdout);
|
||||||
|
filter(f_mail);
|
||||||
|
destination(d_redis_ui_log);
|
||||||
|
};
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
FROM debian:bookworm-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ENV LC_ALL C
|
ENV LC_ALL=C
|
||||||
|
|
||||||
RUN dpkg-divert --local --rename --add /sbin/initctl \
|
RUN dpkg-divert --local --rename --add /sbin/initctl \
|
||||||
&& ln -sf /bin/true /sbin/initctl \
|
&& ln -sf /bin/true /sbin/initctl \
|
||||||
|
|||||||
@@ -390,7 +390,7 @@ hosts = unix:/var/run/mysqld/mysqld.sock
|
|||||||
dbname = ${DBNAME}
|
dbname = ${DBNAME}
|
||||||
query = SELECT goto FROM spamalias
|
query = SELECT goto FROM spamalias
|
||||||
WHERE address='%s'
|
WHERE address='%s'
|
||||||
AND validity >= UNIX_TIMESTAMP()
|
AND (validity >= UNIX_TIMESTAMP() OR permanent != 0)
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
if [ ! -f /opt/postfix/conf/dns_blocklists.cf ]; then
|
if [ ! -f /opt/postfix/conf/dns_blocklists.cf ]; then
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ FROM debian:bookworm-slim
|
|||||||
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ARG RSPAMD_VER=rspamd_3.11.1-1~ab0b44951
|
ARG RSPAMD_VER=rspamd_3.13.2-1~8bf602278
|
||||||
ARG CODENAME=bookworm
|
ARG CODENAME=bookworm
|
||||||
ENV LC_ALL=C
|
ENV LC_ALL=C
|
||||||
|
|
||||||
@@ -14,8 +14,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
dnsutils \
|
dnsutils \
|
||||||
netcat-traditional \
|
netcat-traditional \
|
||||||
wget \
|
wget \
|
||||||
redis-tools \
|
redis-tools \
|
||||||
procps \
|
procps \
|
||||||
nano \
|
nano \
|
||||||
lua-cjson \
|
lua-cjson \
|
||||||
&& arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \
|
&& arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \
|
||||||
|
|||||||
@@ -81,6 +81,29 @@ EOF
|
|||||||
redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning SLAVEOF NO ONE
|
redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning SLAVEOF NO ONE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${SKIP_OLEFY}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
|
if [[ -f /etc/rspamd/local.d/external_services.conf ]]; then
|
||||||
|
rm /etc/rspamd/local.d/external_services.conf
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ ! -f /etc/rspamd/local.d/external_services.conf ]]; then
|
||||||
|
cat <<EOF > /etc/rspamd/local.d/external_services.conf
|
||||||
|
oletools {
|
||||||
|
# default olefy settings
|
||||||
|
servers = "olefy:10055";
|
||||||
|
# needs to be set explicitly for Rspamd < 1.9.5
|
||||||
|
scan_mime_parts = true;
|
||||||
|
# mime-part regex matching in content-type or filename
|
||||||
|
# block all macros
|
||||||
|
extended = true;
|
||||||
|
max_size = 3145728;
|
||||||
|
timeout = 20.0;
|
||||||
|
retransmits = 1;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Provide additional lua modules
|
# Provide additional lua modules
|
||||||
ln -s /usr/lib/$(uname -m)-linux-gnu/liblua5.1-cjson.so.0.0.0 /usr/lib/rspamd/cjson.so
|
ln -s /usr/lib/$(uname -m)-linux-gnu/liblua5.1-cjson.so.0.0.0 /usr/lib/rspamd/cjson.so
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ while [[ "${DBV_NOW}" != "${DBV_NEW}" ]]; do
|
|||||||
done
|
done
|
||||||
echo "DB schema is ${DBV_NOW}"
|
echo "DB schema is ${DBV_NOW}"
|
||||||
|
|
||||||
|
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
|
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TRIGGER IF EXISTS sogo_update_password"
|
||||||
|
fi
|
||||||
|
|
||||||
# cat /dev/urandom seems to hang here occasionally and is not recommended anyway, better use openssl
|
# cat /dev/urandom seems to hang here occasionally and is not recommended anyway, better use openssl
|
||||||
RAND_PASS=$(openssl rand -base64 16 | tr -dc _A-Z-a-z-0-9)
|
RAND_PASS=$(openssl rand -base64 16 | tr -dc _A-Z-a-z-0-9)
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
59,65d58
|
60,65d58
|
||||||
< ng-show="::!activeUser.isSuperUser"
|
|
||||||
< var:ng-click="navButtonClick"
|
< var:ng-click="navButtonClick"
|
||||||
< ng-href="/user">
|
< ng-href="/user">
|
||||||
< <md-icon>build</md-icon>
|
< <md-icon>build</md-icon>
|
||||||
< <md-tooltip><var:string label:value="mailcow"/></md-tooltip>
|
< <md-tooltip>mailcow <var:string label:value="Preferences"/></md-tooltip>
|
||||||
< </md-button>
|
< </md-button>
|
||||||
< <md-button class="md-icon-button"
|
< <md-button class="md-icon-button"
|
||||||
83c76
|
83c76
|
||||||
< onclick="document.getElementById('mc_logout').setAttribute('action', '/'); document.getElementById('mc_logout').submit();"
|
< onclick="mc_logout();"
|
||||||
---
|
---
|
||||||
> ng-show="::activeUser.path.logoff.length"
|
> ng-show="::activeUser.path.logoff.length"
|
||||||
85c78
|
85c78
|
||||||
< ng-href="#">
|
< ng-href="#">
|
||||||
---
|
---
|
||||||
> ng-href="{{::activeUser.path.logoff}}">
|
> ng-href="{{::activeUser.path.logoff}}">
|
||||||
89,91d81
|
|
||||||
< <form method="POST" id="mc_logout" action="user">
|
|
||||||
< <input type="hidden" name="logout" value="1">
|
|
||||||
< </form>
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ RUN apk add --update \
|
|||||||
fcgi \
|
fcgi \
|
||||||
openssl \
|
openssl \
|
||||||
nagios-plugins-mysql \
|
nagios-plugins-mysql \
|
||||||
nagios-plugins-dns \
|
|
||||||
nagios-plugins-disk \
|
nagios-plugins-disk \
|
||||||
bind-tools \
|
bind-tools \
|
||||||
redis \
|
redis \
|
||||||
@@ -32,9 +31,11 @@ RUN apk add --update \
|
|||||||
tzdata \
|
tzdata \
|
||||||
whois \
|
whois \
|
||||||
&& curl https://raw.githubusercontent.com/mludvig/smtp-cli/v3.10/smtp-cli -o /smtp-cli \
|
&& curl https://raw.githubusercontent.com/mludvig/smtp-cli/v3.10/smtp-cli -o /smtp-cli \
|
||||||
&& chmod +x smtp-cli
|
&& chmod +x smtp-cli \
|
||||||
|
&& mkdir /usr/lib/mailcow
|
||||||
|
|
||||||
COPY watchdog.sh /watchdog.sh
|
COPY watchdog.sh /watchdog.sh
|
||||||
COPY check_mysql_slavestatus.sh /usr/lib/nagios/plugins/check_mysql_slavestatus.sh
|
COPY check_mysql_slavestatus.sh /usr/lib/nagios/plugins/check_mysql_slavestatus.sh
|
||||||
|
COPY check_dns.sh /usr/lib/mailcow/check_dns.sh
|
||||||
|
|
||||||
CMD ["/watchdog.sh"]
|
CMD ["/watchdog.sh"]
|
||||||
|
|||||||
39
data/Dockerfiles/watchdog/check_dns.sh
Executable file
39
data/Dockerfiles/watchdog/check_dns.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
while getopts "H:s:" opt; do
|
||||||
|
case "$opt" in
|
||||||
|
H) HOST="$OPTARG" ;;
|
||||||
|
s) SERVER="$OPTARG" ;;
|
||||||
|
*) echo "Usage: $0 -H host -s server"; exit 3 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$SERVER" ]; then
|
||||||
|
echo "No DNS Server provided"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$HOST" ]; then
|
||||||
|
echo "No host to test provided"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run dig and measure the time it takes to run
|
||||||
|
START_TIME=$(date +%s%3N)
|
||||||
|
dig_output=$(dig +short +timeout=2 +tries=1 "$HOST" @"$SERVER" 2>/dev/null)
|
||||||
|
dig_rc=$?
|
||||||
|
dig_output_ips=$(echo "$dig_output" | grep -E '^[0-9.]+$' | sort | paste -sd ',' -)
|
||||||
|
END_TIME=$(date +%s%3N)
|
||||||
|
ELAPSED_TIME=$((END_TIME - START_TIME))
|
||||||
|
|
||||||
|
# validate and perform nagios like output and exit codes
|
||||||
|
if [ $dig_rc -ne 0 ] || [ -z "$dig_output" ]; then
|
||||||
|
echo "Domain $HOST was not found by the server"
|
||||||
|
exit 2
|
||||||
|
elif [ $dig_rc -eq 0 ]; then
|
||||||
|
echo "DNS OK: $ELAPSED_TIME ms response time. $HOST returns $dig_output_ips"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Unknown error"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ "${DEV_MODE}" != "n" ]; then
|
||||||
|
echo -e "\e[31mEnabled Debug Mode\e[0m"
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
trap "exit" INT TERM
|
trap "exit" INT TERM
|
||||||
trap "kill 0" EXIT
|
trap "kill 0" EXIT
|
||||||
|
|
||||||
@@ -297,7 +302,7 @@ unbound_checks() {
|
|||||||
touch /tmp/unbound-mailcow; echo "$(tail -50 /tmp/unbound-mailcow)" > /tmp/unbound-mailcow
|
touch /tmp/unbound-mailcow; echo "$(tail -50 /tmp/unbound-mailcow)" > /tmp/unbound-mailcow
|
||||||
host_ip=$(get_container_ip unbound-mailcow)
|
host_ip=$(get_container_ip unbound-mailcow)
|
||||||
err_c_cur=${err_count}
|
err_c_cur=${err_count}
|
||||||
/usr/lib/nagios/plugins/check_dns -s ${host_ip} -H stackoverflow.com 2>> /tmp/unbound-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
/usr/lib/mailcow/check_dns.sh -s ${host_ip} -H stackoverflow.com 2>> /tmp/unbound-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||||
DNSSEC=$(dig com +dnssec | egrep 'flags:.+ad')
|
DNSSEC=$(dig com +dnssec | egrep 'flags:.+ad')
|
||||||
if [[ -z ${DNSSEC} ]]; then
|
if [[ -z ${DNSSEC} ]]; then
|
||||||
echo "DNSSEC failure" 2>> /tmp/unbound-mailcow 1>&2
|
echo "DNSSEC failure" 2>> /tmp/unbound-mailcow 1>&2
|
||||||
@@ -445,6 +450,31 @@ postfix_checks() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postfix-tlspol_checks() {
|
||||||
|
err_count=0
|
||||||
|
diff_c=0
|
||||||
|
THRESHOLD=${POSTFIX_TLSPOL_THRESHOLD}
|
||||||
|
# Reduce error count by 2 after restarting an unhealthy container
|
||||||
|
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||||
|
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||||
|
touch /tmp/postfix-tlspol-mailcow; echo "$(tail -50 /tmp/postfix-tlspol-mailcow)" > /tmp/postfix-tlspol-mailcow
|
||||||
|
host_ip=$(get_container_ip postfix-tlspol-mailcow)
|
||||||
|
err_c_cur=${err_count}
|
||||||
|
/usr/lib/nagios/plugins/check_tcp -4 -H ${host_ip} -p 8642 2>> /tmp/postfix-tlspol-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
|
||||||
|
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
|
||||||
|
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
|
||||||
|
progress "Postfix TLS Policy companion" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
|
||||||
|
if [[ $? == 10 ]]; then
|
||||||
|
diff_c=0
|
||||||
|
sleep 1
|
||||||
|
else
|
||||||
|
diff_c=0
|
||||||
|
sleep $(( ( RANDOM % 60 ) + 20 ))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
clamd_checks() {
|
clamd_checks() {
|
||||||
err_count=0
|
err_count=0
|
||||||
diff_c=0
|
diff_c=0
|
||||||
@@ -922,6 +952,18 @@ PID=$!
|
|||||||
echo "Spawned mailq_checks with PID ${PID}"
|
echo "Spawned mailq_checks with PID ${PID}"
|
||||||
BACKGROUND_TASKS+=(${PID})
|
BACKGROUND_TASKS+=(${PID})
|
||||||
|
|
||||||
|
(
|
||||||
|
while true; do
|
||||||
|
if ! postfix-tlspol_checks; then
|
||||||
|
log_msg "Postfix TLS Policy hit error limit"
|
||||||
|
echo postfix-tlspol-mailcow > /tmp/com_pipe
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
PID=$!
|
||||||
|
echo "Spawned postfix-tlspol_checks with PID ${PID}"
|
||||||
|
BACKGROUND_TASKS+=(${PID})
|
||||||
|
|
||||||
(
|
(
|
||||||
while true; do
|
while true; do
|
||||||
if ! dovecot_checks; then
|
if ! dovecot_checks; then
|
||||||
@@ -994,6 +1036,7 @@ PID=$!
|
|||||||
echo "Spawned cert_checks with PID ${PID}"
|
echo "Spawned cert_checks with PID ${PID}"
|
||||||
BACKGROUND_TASKS+=(${PID})
|
BACKGROUND_TASKS+=(${PID})
|
||||||
|
|
||||||
|
if [[ "${SKIP_OLEFY}" =~ ^([nN][oO]|[nN])+$ ]]; then
|
||||||
(
|
(
|
||||||
while true; do
|
while true; do
|
||||||
if ! olefy_checks; then
|
if ! olefy_checks; then
|
||||||
@@ -1005,6 +1048,7 @@ done
|
|||||||
PID=$!
|
PID=$!
|
||||||
echo "Spawned olefy_checks with PID ${PID}"
|
echo "Spawned olefy_checks with PID ${PID}"
|
||||||
BACKGROUND_TASKS+=(${PID})
|
BACKGROUND_TASKS+=(${PID})
|
||||||
|
fi
|
||||||
|
|
||||||
(
|
(
|
||||||
while true; do
|
while true; do
|
||||||
|
|||||||
@@ -69,36 +69,43 @@ require_once 'functions.acl.inc.php';
|
|||||||
|
|
||||||
$isSOGoRequest = $post['real_rip'] == getenv('IPV4_NETWORK') . '.248';
|
$isSOGoRequest = $post['real_rip'] == getenv('IPV4_NETWORK') . '.248';
|
||||||
$result = false;
|
$result = false;
|
||||||
$protocol = $post['protocol'];
|
|
||||||
if ($isSOGoRequest) {
|
if ($isSOGoRequest) {
|
||||||
$protocol = null;
|
|
||||||
// This is a SOGo Auth request. First check for SSO password.
|
// This is a SOGo Auth request. First check for SSO password.
|
||||||
$sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass");
|
$sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass");
|
||||||
if ($sogo_sso_pass === $post['password']){
|
if ($sogo_sso_pass === $post['password']){
|
||||||
error_log('MAILCOWAUTH: SOGo SSO auth for user ' . $post['username']);
|
error_log('MAILCOWAUTH: SOGo SSO auth for user ' . $post['username']);
|
||||||
|
set_sasl_log($post['username'], $post['real_rip'], "SOGO");
|
||||||
$result = true;
|
$result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($result === false){
|
if ($result === false){
|
||||||
$result = apppass_login($post['username'], $post['password'], $protocol, array(
|
// If it's a SOGo Request, don't check for protocol access
|
||||||
|
$service = ($isSOGoRequest) ? false : array($post['service'] => true);
|
||||||
|
$result = apppass_login($post['username'], $post['password'], $service, array(
|
||||||
'is_internal' => true,
|
'is_internal' => true,
|
||||||
'remote_addr' => $post['real_rip']
|
'remote_addr' => $post['real_rip']
|
||||||
));
|
));
|
||||||
if ($result) error_log('MAILCOWAUTH: App auth for user ' . $post['username']);
|
if ($result) {
|
||||||
|
error_log('MAILCOWAUTH: App auth for user ' . $post['username'] . " with service " . $post['service'] . " from IP " . $post['real_rip']);
|
||||||
|
set_sasl_log($post['username'], $post['real_rip'], $post['service']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($result === false){
|
if ($result === false){
|
||||||
// Init Identity Provider
|
// Init Identity Provider
|
||||||
$iam_provider = identity_provider('init');
|
$iam_provider = identity_provider('init');
|
||||||
$iam_settings = identity_provider('get');
|
$iam_settings = identity_provider('get');
|
||||||
$result = user_login($post['username'], $post['password'], array('is_internal' => true));
|
$result = user_login($post['username'], $post['password'], array('is_internal' => true, 'service' => $post['service']));
|
||||||
if ($result) error_log('MAILCOWAUTH: User auth for user ' . $post['username']);
|
if ($result) {
|
||||||
|
error_log('MAILCOWAUTH: User auth for user ' . $post['username'] . " with service " . $post['service'] . " from IP " . $post['real_rip']);
|
||||||
|
set_sasl_log($post['username'], $post['real_rip'], $post['service']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
http_response_code(200); // OK
|
http_response_code(200); // OK
|
||||||
$return['success'] = true;
|
$return['success'] = true;
|
||||||
} else {
|
} else {
|
||||||
error_log("MAILCOWAUTH: Login failed for user " . $post['username']);
|
error_log("MAILCOWAUTH: Login failed for user " . $post['username'] . " with service " . $post['service'] . " from IP " . $post['real_rip']);
|
||||||
http_response_code(401); // Unauthorized
|
http_response_code(401); // Unauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,21 +3,20 @@ function auth_password_verify(request, password)
|
|||||||
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user"
|
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user"
|
||||||
end
|
end
|
||||||
|
|
||||||
json = require "cjson"
|
local json = require "cjson"
|
||||||
ltn12 = require "ltn12"
|
local ltn12 = require "ltn12"
|
||||||
https = require "ssl.https"
|
local https = require "ssl.https"
|
||||||
https.TIMEOUT = 5
|
https.TIMEOUT = 30
|
||||||
|
|
||||||
local req = {
|
local req = {
|
||||||
username = request.user,
|
username = request.user,
|
||||||
password = password,
|
password = password,
|
||||||
real_rip = request.real_rip,
|
real_rip = request.real_rip,
|
||||||
protocol = {}
|
service = request.service
|
||||||
}
|
}
|
||||||
req.protocol[request.service] = true
|
|
||||||
local req_json = json.encode(req)
|
local req_json = json.encode(req)
|
||||||
local res = {}
|
local res = {}
|
||||||
|
|
||||||
local b, c = https.request {
|
local b, c = https.request {
|
||||||
method = "POST",
|
method = "POST",
|
||||||
url = "https://nginx:9082",
|
url = "https://nginx:9082",
|
||||||
@@ -29,11 +28,27 @@ function auth_password_verify(request, password)
|
|||||||
sink = ltn12.sink.table(res),
|
sink = ltn12.sink.table(res),
|
||||||
insecure = true
|
insecure = true
|
||||||
}
|
}
|
||||||
local api_response = json.decode(table.concat(res))
|
|
||||||
if api_response.success == true then
|
-- Returning PASSDB_RESULT_PASSWORD_MISMATCH will reset the user's auth cache entry.
|
||||||
|
-- Returning PASSDB_RESULT_INTERNAL_FAILURE keeps the existing cache entry,
|
||||||
|
-- even if the TTL has expired. Useful to avoid cache eviction during backend issues.
|
||||||
|
if c ~= 200 and c ~= 401 then
|
||||||
|
dovecot.i_info("HTTP request failed with " .. c .. " for user " .. request.user)
|
||||||
|
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Upstream error"
|
||||||
|
end
|
||||||
|
|
||||||
|
local response_str = table.concat(res)
|
||||||
|
local is_response_valid, response_json = pcall(json.decode, response_str)
|
||||||
|
|
||||||
|
if not is_response_valid then
|
||||||
|
dovecot.i_info("Invalid JSON received: " .. response_str)
|
||||||
|
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Invalid response format"
|
||||||
|
end
|
||||||
|
|
||||||
|
if response_json.success == true then
|
||||||
return dovecot.auth.PASSDB_RESULT_OK, ""
|
return dovecot.auth.PASSDB_RESULT_OK, ""
|
||||||
end
|
end
|
||||||
|
|
||||||
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate"
|
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ mail_shared_explicit_inbox = yes
|
|||||||
mail_prefetch_count = 30
|
mail_prefetch_count = 30
|
||||||
passdb {
|
passdb {
|
||||||
driver = lua
|
driver = lua
|
||||||
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes cache_key=%u:%w
|
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes cache_key=%s:%u:%w
|
||||||
result_success = return-ok
|
result_success = return-ok
|
||||||
result_failure = continue
|
result_failure = continue
|
||||||
result_internalfail = continue
|
result_internalfail = continue
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ events {
|
|||||||
http {
|
http {
|
||||||
include /etc/nginx/mime.types;
|
include /etc/nginx/mime.types;
|
||||||
default_type application/octet-stream;
|
default_type application/octet-stream;
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
'$status $body_bytes_sent "$http_referer" '
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
@@ -48,13 +49,21 @@ http {
|
|||||||
listen {{ HTTP_PORT }} default_server;
|
listen {{ HTTP_PORT }} default_server;
|
||||||
listen [::]:{{ HTTP_PORT }} default_server;
|
listen [::]:{{ HTTP_PORT }} default_server;
|
||||||
|
|
||||||
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* {{ ADDITIONAL_SERVER_NAMES | join(' ') }};
|
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* mta-sts.* {{ ADDITIONAL_SERVER_NAMES | join(' ') }};
|
||||||
|
|
||||||
if ( $request_uri ~* "%0A|%0D" ) { return 403; }
|
if ( $request_uri ~* "%0A|%0D" ) { return 403; }
|
||||||
location ^~ /.well-known/acme-challenge/ {
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
allow all;
|
allow all;
|
||||||
default_type "text/plain";
|
default_type "text/plain";
|
||||||
}
|
}
|
||||||
|
location ^~ /.well-known/mta-sts.txt {
|
||||||
|
allow all;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass {{ PHPFPMHOST }}:9002;
|
||||||
|
include /etc/nginx/fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root/mta-sts.php;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
location / {
|
location / {
|
||||||
return 301 https://$host$uri$is_args$args;
|
return 301 https://$host$uri$is_args$args;
|
||||||
}
|
}
|
||||||
@@ -70,7 +79,7 @@ http {
|
|||||||
{%endif%}
|
{%endif%}
|
||||||
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
|
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
|
||||||
|
|
||||||
{% if not DISABLE_IPv6 %}
|
{% if ENABLE_IPV6 %}
|
||||||
{% if not HTTP_REDIRECT %}
|
{% if not HTTP_REDIRECT %}
|
||||||
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
|
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
|
||||||
{%endif%}
|
{%endif%}
|
||||||
@@ -82,7 +91,7 @@ http {
|
|||||||
ssl_certificate /etc/ssl/mail/cert.pem;
|
ssl_certificate /etc/ssl/mail/cert.pem;
|
||||||
ssl_certificate_key /etc/ssl/mail/key.pem;
|
ssl_certificate_key /etc/ssl/mail/key.pem;
|
||||||
|
|
||||||
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.*;
|
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* mta-sts.*;
|
||||||
|
|
||||||
include /etc/nginx/includes/sites-default.conf;
|
include /etc/nginx/includes/sites-default.conf;
|
||||||
}
|
}
|
||||||
@@ -97,7 +106,7 @@ http {
|
|||||||
{%endif%}
|
{%endif%}
|
||||||
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
|
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
|
||||||
|
|
||||||
{% if not DISABLE_IPv6 %}
|
{% if ENABLE_IPV6 %}
|
||||||
{% if not HTTP_REDIRECT %}
|
{% if not HTTP_REDIRECT %}
|
||||||
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
|
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
|
||||||
{%endif%}
|
{%endif%}
|
||||||
@@ -118,7 +127,7 @@ http {
|
|||||||
# rspamd dynmaps:
|
# rspamd dynmaps:
|
||||||
server {
|
server {
|
||||||
listen 8081;
|
listen 8081;
|
||||||
{% if not DISABLE_IPv6 %}
|
{% if ENABLE_IPV6 %}
|
||||||
listen [::]:8081;
|
listen [::]:8081;
|
||||||
{%endif%}
|
{%endif%}
|
||||||
index index.php index.html;
|
index index.php index.html;
|
||||||
@@ -191,7 +200,7 @@ http {
|
|||||||
{%endif%}
|
{%endif%}
|
||||||
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
|
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
|
||||||
|
|
||||||
{% if not DISABLE_IPv6 %}
|
{% if ENABLE_IPV6 %}
|
||||||
{% if not HTTP_REDIRECT %}
|
{% if not HTTP_REDIRECT %}
|
||||||
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
|
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
|
||||||
{%endif%}
|
{%endif%}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ ssl_session_tickets off;
|
|||||||
|
|
||||||
add_header Strict-Transport-Security "max-age=15768000;";
|
add_header Strict-Transport-Security "max-age=15768000;";
|
||||||
add_header X-Content-Type-Options nosniff;
|
add_header X-Content-Type-Options nosniff;
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
||||||
add_header X-Robots-Tag none;
|
add_header X-Robots-Tag none;
|
||||||
add_header X-Download-Options noopen;
|
add_header X-Download-Options noopen;
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
@@ -76,6 +75,14 @@ location ^~ /.well-known/acme-challenge/ {
|
|||||||
allow all;
|
allow all;
|
||||||
default_type "text/plain";
|
default_type "text/plain";
|
||||||
}
|
}
|
||||||
|
location ^~ /.well-known/mta-sts.txt {
|
||||||
|
allow all;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass {{ PHPFPMHOST }}:9002;
|
||||||
|
include /etc/nginx/fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root/mta-sts.php;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
|
||||||
rewrite ^/.well-known/caldav$ /SOGo/dav/ permanent;
|
rewrite ^/.well-known/caldav$ /SOGo/dav/ permanent;
|
||||||
rewrite ^/.well-known/carddav$ /SOGo/dav/ permanent;
|
rewrite ^/.well-known/carddav$ /SOGo/dav/ permanent;
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ while (true) {
|
|||||||
logMsg("err", "Could not create user " . $user['email']);
|
logMsg("err", "Could not create user " . $user['email']);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if ($row && intval($iam_settings['periodic_sync']) == 1) {
|
} else if ($row && intval($iam_settings['periodic_sync']) == 1 && $row['authsource'] == "keycloak") {
|
||||||
if ($mapper_key === false){
|
if ($mapper_key === false){
|
||||||
logMsg("warning", "No matching attribute mapping found for user " . $user['email']);
|
logMsg("warning", "No matching attribute mapping found for user " . $user['email']);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ foreach ($response as $user) {
|
|||||||
logMsg("err", "Could not create user " . $user[$iam_settings['username_field']][0]);
|
logMsg("err", "Could not create user " . $user[$iam_settings['username_field']][0]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if ($row && intval($iam_settings['periodic_sync']) == 1) {
|
} else if ($row && intval($iam_settings['periodic_sync']) == 1 && $row['authsource'] == "ldap") {
|
||||||
if ($mapper_key === false){
|
if ($mapper_key === false){
|
||||||
logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]);
|
logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
|
; NOTE: Restart phpfpm on ANY manual changes to PHP files!
|
||||||
|
|
||||||
|
; opcache
|
||||||
opcache.enable=1
|
opcache.enable=1
|
||||||
opcache.enable_cli=1
|
opcache.enable_cli=1
|
||||||
opcache.interned_strings_buffer=16
|
opcache.interned_strings_buffer=16
|
||||||
opcache.max_accelerated_files=10000
|
opcache.max_accelerated_files=10000
|
||||||
opcache.memory_consumption=128
|
opcache.memory_consumption=128
|
||||||
opcache.save_comments=1
|
opcache.save_comments=1
|
||||||
opcache.revalidate_freq=1
|
opcache.validate_timestamps=0
|
||||||
|
|
||||||
|
; JIT
|
||||||
|
; Disabled for now due to some PHP segmentation faults observed
|
||||||
|
; in certain environments. Possibly some PHP or PHP extension bug.
|
||||||
|
opcache.jit=disable
|
||||||
|
opcache.jit_buffer_size=0
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ smtp_sasl_auth_enable = yes
|
|||||||
smtp_sasl_password_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf
|
smtp_sasl_password_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf
|
||||||
smtp_sasl_security_options =
|
smtp_sasl_security_options =
|
||||||
smtp_sasl_mechanism_filter = plain, login
|
smtp_sasl_mechanism_filter = plain, login
|
||||||
smtp_tls_policy_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf
|
smtp_tls_policy_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf socketmap:inet:postfix-tlspol:8642:QUERY
|
||||||
smtp_header_checks = pcre:/opt/postfix/conf/anonymize_headers.pcre
|
smtp_header_checks = pcre:/opt/postfix/conf/anonymize_headers.pcre
|
||||||
mail_name = Postcow
|
mail_name = Postcow
|
||||||
# local_transport map catches local destinations and prevents routing local dests when the next map would route "*"
|
# local_transport map catches local destinations and prevents routing local dests when the next map would route "*"
|
||||||
|
|||||||
@@ -1,13 +1,26 @@
|
|||||||
# Whitelist generated by Postwhite v3.4 on Sat Mar 1 00:19:29 UTC 2025
|
# Whitelist generated by Postwhite v3.4 on Mon Dec 1 00:24:43 UTC 2025
|
||||||
# https://github.com/stevejenkins/postwhite/
|
# https://github.com/stevejenkins/postwhite/
|
||||||
# 2000 total rules
|
# 2186 total rules
|
||||||
2a00:1450:4000::/36 permit
|
2a00:1450:4000::/36 permit
|
||||||
2a01:111:f400::/48 permit
|
2a01:111:f400::/48 permit
|
||||||
2a01:111:f403:8000::/50 permit
|
2a01:111:f403:2800::/53 permit
|
||||||
2a01:111:f403:8000::/51 permit
|
2a01:111:f403:8000::/51 permit
|
||||||
2a01:111:f403::/49 permit
|
2a01:111:f403::/49 permit
|
||||||
2a01:111:f403:c000::/51 permit
|
2a01:111:f403:c000::/51 permit
|
||||||
|
2a01:111:f403:d000::/53 permit
|
||||||
2a01:111:f403:f000::/52 permit
|
2a01:111:f403:f000::/52 permit
|
||||||
|
2a01:238:20a:202:5370::1 permit
|
||||||
|
2a01:238:20a:202:5372::1 permit
|
||||||
|
2a01:238:20a:202:5373::1 permit
|
||||||
|
2a01:238:400:101:53::1 permit
|
||||||
|
2a01:238:400:102:53::1 permit
|
||||||
|
2a01:238:400:103:53::1 permit
|
||||||
|
2a01:238:400:301:53::1 permit
|
||||||
|
2a01:238:400:302:53::1 permit
|
||||||
|
2a01:238:400:303:53::1 permit
|
||||||
|
2a01:238:400:470:53::1 permit
|
||||||
|
2a01:238:400:471:53::1 permit
|
||||||
|
2a01:238:400:472:53::1 permit
|
||||||
2a01:b747:3000:200::/56 permit
|
2a01:b747:3000:200::/56 permit
|
||||||
2a01:b747:3001:200::/56 permit
|
2a01:b747:3001:200::/56 permit
|
||||||
2a01:b747:3002:200::/56 permit
|
2a01:b747:3002:200::/56 permit
|
||||||
@@ -16,23 +29,43 @@
|
|||||||
2a01:b747:3005:200::/56 permit
|
2a01:b747:3005:200::/56 permit
|
||||||
2a01:b747:3006:200::/56 permit
|
2a01:b747:3006:200::/56 permit
|
||||||
2a02:a60:0:5::/64 permit
|
2a02:a60:0:5::/64 permit
|
||||||
|
2a0f:f640::/56 permit
|
||||||
2c0f:fb50:4000::/36 permit
|
2c0f:fb50:4000::/36 permit
|
||||||
2.207.151.53 permit
|
2.207.151.53 permit
|
||||||
|
2.207.217.30 permit
|
||||||
|
3.64.237.68 permit
|
||||||
|
3.65.3.180 permit
|
||||||
3.70.123.177 permit
|
3.70.123.177 permit
|
||||||
|
3.72.182.33 permit
|
||||||
|
3.74.81.189 permit
|
||||||
|
3.74.125.228 permit
|
||||||
|
3.75.33.185 permit
|
||||||
3.93.157.0/24 permit
|
3.93.157.0/24 permit
|
||||||
3.94.40.108 permit
|
3.94.40.108 permit
|
||||||
|
3.121.107.214 permit
|
||||||
3.129.120.190 permit
|
3.129.120.190 permit
|
||||||
3.210.190.0/24 permit
|
3.210.190.0/24 permit
|
||||||
|
3.211.80.218 permit
|
||||||
|
3.216.221.67 permit
|
||||||
|
3.221.209.22 permit
|
||||||
8.20.114.31 permit
|
8.20.114.31 permit
|
||||||
8.25.194.0/23 permit
|
8.25.194.0/23 permit
|
||||||
8.25.196.0/23 permit
|
8.25.196.0/23 permit
|
||||||
|
8.36.116.0/24 permit
|
||||||
|
8.39.144.0/24 permit
|
||||||
12.130.86.238 permit
|
12.130.86.238 permit
|
||||||
|
13.107.213.69 permit
|
||||||
|
13.107.246.69 permit
|
||||||
|
13.108.16.0/20 permit
|
||||||
13.110.208.0/21 permit
|
13.110.208.0/21 permit
|
||||||
13.110.209.0/24 permit
|
13.110.209.0/24 permit
|
||||||
13.110.216.0/22 permit
|
13.110.216.0/22 permit
|
||||||
13.110.224.0/20 permit
|
13.110.224.0/20 permit
|
||||||
13.111.0.0/16 permit
|
13.111.0.0/16 permit
|
||||||
13.111.191.0/24 permit
|
13.111.191.0/24 permit
|
||||||
|
13.216.7.111 permit
|
||||||
|
13.216.54.180 permit
|
||||||
|
13.247.164.219 permit
|
||||||
15.200.21.50 permit
|
15.200.21.50 permit
|
||||||
15.200.44.248 permit
|
15.200.44.248 permit
|
||||||
15.200.201.185 permit
|
15.200.201.185 permit
|
||||||
@@ -45,23 +78,26 @@
|
|||||||
18.97.1.184/29 permit
|
18.97.1.184/29 permit
|
||||||
18.97.2.64/26 permit
|
18.97.2.64/26 permit
|
||||||
18.156.89.250 permit
|
18.156.89.250 permit
|
||||||
|
18.156.205.64 permit
|
||||||
|
18.157.70.148 permit
|
||||||
|
18.157.114.255 permit
|
||||||
18.157.243.190 permit
|
18.157.243.190 permit
|
||||||
|
18.158.153.154 permit
|
||||||
18.194.95.56 permit
|
18.194.95.56 permit
|
||||||
|
18.197.217.180 permit
|
||||||
18.198.96.88 permit
|
18.198.96.88 permit
|
||||||
|
18.199.210.3 permit
|
||||||
|
18.207.52.234 permit
|
||||||
18.208.124.128/25 permit
|
18.208.124.128/25 permit
|
||||||
18.216.232.154 permit
|
18.216.232.154 permit
|
||||||
18.235.27.253 permit
|
18.235.27.253 permit
|
||||||
18.236.40.242 permit
|
18.236.40.242 permit
|
||||||
18.236.56.161 permit
|
|
||||||
20.51.6.32/30 permit
|
20.51.6.32/30 permit
|
||||||
20.51.98.61 permit
|
|
||||||
20.52.52.2 permit
|
20.52.52.2 permit
|
||||||
20.52.128.133 permit
|
20.52.128.133 permit
|
||||||
20.59.80.4/30 permit
|
20.59.80.4/30 permit
|
||||||
20.63.210.192/28 permit
|
20.63.210.192/28 permit
|
||||||
20.69.8.108/30 permit
|
20.69.8.108/30 permit
|
||||||
20.70.246.20 permit
|
|
||||||
20.76.201.171 permit
|
|
||||||
20.83.222.104/30 permit
|
20.83.222.104/30 permit
|
||||||
20.88.157.184/30 permit
|
20.88.157.184/30 permit
|
||||||
20.94.180.64/28 permit
|
20.94.180.64/28 permit
|
||||||
@@ -70,14 +106,11 @@
|
|||||||
20.98.194.68/30 permit
|
20.98.194.68/30 permit
|
||||||
20.105.209.76/30 permit
|
20.105.209.76/30 permit
|
||||||
20.107.239.64/30 permit
|
20.107.239.64/30 permit
|
||||||
20.112.250.133 permit
|
|
||||||
20.118.139.208/30 permit
|
20.118.139.208/30 permit
|
||||||
20.141.10.196 permit
|
20.141.10.196 permit
|
||||||
20.185.214.0/27 permit
|
20.185.214.0/27 permit
|
||||||
20.185.214.32/27 permit
|
20.185.214.32/27 permit
|
||||||
20.185.214.64/27 permit
|
20.185.214.64/27 permit
|
||||||
20.231.239.246 permit
|
|
||||||
20.236.44.162 permit
|
|
||||||
23.103.224.0/19 permit
|
23.103.224.0/19 permit
|
||||||
23.249.208.0/20 permit
|
23.249.208.0/20 permit
|
||||||
23.251.224.0/19 permit
|
23.251.224.0/19 permit
|
||||||
@@ -89,6 +122,7 @@
|
|||||||
23.253.183.147 permit
|
23.253.183.147 permit
|
||||||
23.253.183.148 permit
|
23.253.183.148 permit
|
||||||
23.253.183.150 permit
|
23.253.183.150 permit
|
||||||
|
24.110.64.0/18 permit
|
||||||
27.123.204.128/30 permit
|
27.123.204.128/30 permit
|
||||||
27.123.204.132/31 permit
|
27.123.204.132/31 permit
|
||||||
27.123.204.148/30 permit
|
27.123.204.148/30 permit
|
||||||
@@ -101,19 +135,50 @@
|
|||||||
27.123.206.56/29 permit
|
27.123.206.56/29 permit
|
||||||
27.123.206.76/30 permit
|
27.123.206.76/30 permit
|
||||||
27.123.206.80/28 permit
|
27.123.206.80/28 permit
|
||||||
31.25.48.222 permit
|
|
||||||
31.47.251.17 permit
|
31.47.251.17 permit
|
||||||
|
31.186.239.0/24 permit
|
||||||
|
34.2.64.0/22 permit
|
||||||
|
34.2.68.0/23 permit
|
||||||
|
34.2.70.0/23 permit
|
||||||
|
34.2.71.64/26 permit
|
||||||
|
34.2.72.0/22 permit
|
||||||
|
34.2.75.0/26 permit
|
||||||
|
34.2.78.0/23 permit
|
||||||
|
34.2.80.0/23 permit
|
||||||
|
34.2.82.0/23 permit
|
||||||
|
34.2.84.0/24 permit
|
||||||
|
34.2.84.64/26 permit
|
||||||
|
34.2.85.0/24 permit
|
||||||
|
34.2.85.64/26 permit
|
||||||
|
34.2.86.0/23 permit
|
||||||
|
34.2.88.0/23 permit
|
||||||
|
34.2.90.0/23 permit
|
||||||
|
34.2.92.0/23 permit
|
||||||
|
34.2.94.0/23 permit
|
||||||
|
34.70.158.162 permit
|
||||||
|
34.74.74.140 permit
|
||||||
|
34.83.159.189 permit
|
||||||
|
34.141.160.224 permit
|
||||||
|
34.193.58.168 permit
|
||||||
34.195.217.107 permit
|
34.195.217.107 permit
|
||||||
|
34.197.10.50 permit
|
||||||
|
34.197.254.9 permit
|
||||||
|
34.198.94.229 permit
|
||||||
|
34.198.218.121 permit
|
||||||
34.212.163.75 permit
|
34.212.163.75 permit
|
||||||
34.215.104.144 permit
|
34.215.104.144 permit
|
||||||
34.218.116.3 permit
|
34.218.115.239 permit
|
||||||
34.225.212.172 permit
|
34.225.212.172 permit
|
||||||
|
35.83.148.184 permit
|
||||||
|
35.155.198.111 permit
|
||||||
|
35.158.23.94 permit
|
||||||
35.161.32.253 permit
|
35.161.32.253 permit
|
||||||
|
35.162.73.231 permit
|
||||||
35.167.93.243 permit
|
35.167.93.243 permit
|
||||||
|
35.174.145.124 permit
|
||||||
35.176.132.251 permit
|
35.176.132.251 permit
|
||||||
35.190.247.0/24 permit
|
|
||||||
35.191.0.0/16 permit
|
|
||||||
35.205.92.9 permit
|
35.205.92.9 permit
|
||||||
|
35.228.216.85 permit
|
||||||
35.242.169.159 permit
|
35.242.169.159 permit
|
||||||
37.188.97.188 permit
|
37.188.97.188 permit
|
||||||
37.218.248.47 permit
|
37.218.248.47 permit
|
||||||
@@ -128,11 +193,19 @@
|
|||||||
40.233.83.78 permit
|
40.233.83.78 permit
|
||||||
40.233.88.28 permit
|
40.233.88.28 permit
|
||||||
44.206.138.57 permit
|
44.206.138.57 permit
|
||||||
|
44.210.169.44 permit
|
||||||
44.217.45.156 permit
|
44.217.45.156 permit
|
||||||
44.236.56.93 permit
|
44.236.56.93 permit
|
||||||
44.238.220.251 permit
|
44.238.220.251 permit
|
||||||
|
44.245.243.92 permit
|
||||||
|
44.246.1.125 permit
|
||||||
|
44.246.68.102 permit
|
||||||
|
44.246.77.92 permit
|
||||||
45.14.148.0/22 permit
|
45.14.148.0/22 permit
|
||||||
46.19.170.16 permit
|
45.143.132.0/24 permit
|
||||||
|
45.143.133.0/24 permit
|
||||||
|
45.143.134.0/24 permit
|
||||||
|
45.143.135.0/24 permit
|
||||||
46.226.48.0/21 permit
|
46.226.48.0/21 permit
|
||||||
46.228.36.37 permit
|
46.228.36.37 permit
|
||||||
46.228.36.38/31 permit
|
46.228.36.38/31 permit
|
||||||
@@ -183,6 +256,7 @@
|
|||||||
46.243.88.177 permit
|
46.243.88.177 permit
|
||||||
46.243.95.179 permit
|
46.243.95.179 permit
|
||||||
46.243.95.180 permit
|
46.243.95.180 permit
|
||||||
|
50.16.246.183 permit
|
||||||
50.18.45.249 permit
|
50.18.45.249 permit
|
||||||
50.18.121.236 permit
|
50.18.121.236 permit
|
||||||
50.18.121.248 permit
|
50.18.121.248 permit
|
||||||
@@ -196,14 +270,23 @@
|
|||||||
50.56.130.220 permit
|
50.56.130.220 permit
|
||||||
50.56.130.221 permit
|
50.56.130.221 permit
|
||||||
50.56.130.222 permit
|
50.56.130.222 permit
|
||||||
|
50.112.246.219 permit
|
||||||
52.1.14.157 permit
|
52.1.14.157 permit
|
||||||
52.5.230.59 permit
|
52.5.230.59 permit
|
||||||
|
52.12.53.23 permit
|
||||||
|
52.13.214.179 permit
|
||||||
|
52.26.1.71 permit
|
||||||
52.27.5.72 permit
|
52.27.5.72 permit
|
||||||
52.27.28.47 permit
|
52.27.28.47 permit
|
||||||
52.28.63.81 permit
|
52.28.63.81 permit
|
||||||
|
52.28.197.132 permit
|
||||||
|
52.34.181.151 permit
|
||||||
|
52.35.192.45 permit
|
||||||
52.36.138.31 permit
|
52.36.138.31 permit
|
||||||
52.37.142.146 permit
|
52.37.142.146 permit
|
||||||
|
52.42.203.116 permit
|
||||||
52.50.24.208 permit
|
52.50.24.208 permit
|
||||||
|
52.57.120.243 permit
|
||||||
52.58.216.183 permit
|
52.58.216.183 permit
|
||||||
52.59.143.3 permit
|
52.59.143.3 permit
|
||||||
52.60.41.5 permit
|
52.60.41.5 permit
|
||||||
@@ -216,7 +299,6 @@
|
|||||||
52.96.91.34 permit
|
52.96.91.34 permit
|
||||||
52.96.111.82 permit
|
52.96.111.82 permit
|
||||||
52.96.172.98 permit
|
52.96.172.98 permit
|
||||||
52.96.214.50 permit
|
|
||||||
52.96.222.194 permit
|
52.96.222.194 permit
|
||||||
52.96.222.226 permit
|
52.96.222.226 permit
|
||||||
52.96.223.2 permit
|
52.96.223.2 permit
|
||||||
@@ -246,23 +328,23 @@
|
|||||||
54.174.63.0/24 permit
|
54.174.63.0/24 permit
|
||||||
54.186.193.102 permit
|
54.186.193.102 permit
|
||||||
54.191.223.56 permit
|
54.191.223.56 permit
|
||||||
|
54.211.126.101 permit
|
||||||
54.213.20.246 permit
|
54.213.20.246 permit
|
||||||
54.214.39.184 permit
|
54.214.39.184 permit
|
||||||
54.240.0.0/18 permit
|
54.240.0.0/18 permit
|
||||||
54.240.64.0/19 permit
|
54.240.64.0/18 permit
|
||||||
54.240.96.0/19 permit
|
|
||||||
54.241.16.209 permit
|
54.241.16.209 permit
|
||||||
54.244.54.130 permit
|
54.244.54.130 permit
|
||||||
54.244.242.0/24 permit
|
54.244.242.0/24 permit
|
||||||
54.255.61.23 permit
|
54.255.61.23 permit
|
||||||
57.103.64.0/18 permit
|
57.103.64.0/18 permit
|
||||||
|
57.129.93.249 permit
|
||||||
62.13.128.0/24 permit
|
62.13.128.0/24 permit
|
||||||
62.13.129.128/25 permit
|
62.13.129.128/25 permit
|
||||||
62.13.136.0/21 permit
|
62.13.136.0/21 permit
|
||||||
62.13.144.0/21 permit
|
62.13.144.0/21 permit
|
||||||
62.13.152.0/21 permit
|
62.13.152.0/21 permit
|
||||||
62.17.146.128/26 permit
|
62.17.146.128/26 permit
|
||||||
62.179.121.0/24 permit
|
|
||||||
62.201.172.0/27 permit
|
62.201.172.0/27 permit
|
||||||
62.201.172.32/27 permit
|
62.201.172.32/27 permit
|
||||||
62.253.227.114 permit
|
62.253.227.114 permit
|
||||||
@@ -270,6 +352,9 @@
|
|||||||
63.128.21.0/24 permit
|
63.128.21.0/24 permit
|
||||||
63.143.57.128/25 permit
|
63.143.57.128/25 permit
|
||||||
63.143.59.128/25 permit
|
63.143.59.128/25 permit
|
||||||
|
63.176.194.123 permit
|
||||||
|
63.178.132.221 permit
|
||||||
|
63.178.143.178 permit
|
||||||
64.18.0.0/20 permit
|
64.18.0.0/20 permit
|
||||||
64.20.241.45 permit
|
64.20.241.45 permit
|
||||||
64.69.212.0/24 permit
|
64.69.212.0/24 permit
|
||||||
@@ -282,6 +367,7 @@
|
|||||||
64.127.115.252 permit
|
64.127.115.252 permit
|
||||||
64.132.88.0/23 permit
|
64.132.88.0/23 permit
|
||||||
64.132.92.0/24 permit
|
64.132.92.0/24 permit
|
||||||
|
64.181.194.190 permit
|
||||||
64.207.219.7 permit
|
64.207.219.7 permit
|
||||||
64.207.219.8 permit
|
64.207.219.8 permit
|
||||||
64.207.219.9 permit
|
64.207.219.9 permit
|
||||||
@@ -291,9 +377,6 @@
|
|||||||
64.207.219.13 permit
|
64.207.219.13 permit
|
||||||
64.207.219.14 permit
|
64.207.219.14 permit
|
||||||
64.207.219.15 permit
|
64.207.219.15 permit
|
||||||
64.207.219.24 permit
|
|
||||||
64.207.219.25 permit
|
|
||||||
64.207.219.26 permit
|
|
||||||
64.207.219.71 permit
|
64.207.219.71 permit
|
||||||
64.207.219.72 permit
|
64.207.219.72 permit
|
||||||
64.207.219.73 permit
|
64.207.219.73 permit
|
||||||
@@ -303,9 +386,6 @@
|
|||||||
64.207.219.77 permit
|
64.207.219.77 permit
|
||||||
64.207.219.78 permit
|
64.207.219.78 permit
|
||||||
64.207.219.79 permit
|
64.207.219.79 permit
|
||||||
64.207.219.88 permit
|
|
||||||
64.207.219.89 permit
|
|
||||||
64.207.219.90 permit
|
|
||||||
64.207.219.135 permit
|
64.207.219.135 permit
|
||||||
64.207.219.136 permit
|
64.207.219.136 permit
|
||||||
64.207.219.137 permit
|
64.207.219.137 permit
|
||||||
@@ -317,23 +397,15 @@
|
|||||||
64.207.219.143 permit
|
64.207.219.143 permit
|
||||||
64.233.160.0/19 permit
|
64.233.160.0/19 permit
|
||||||
65.52.80.137 permit
|
65.52.80.137 permit
|
||||||
65.54.51.64/26 permit
|
|
||||||
65.54.61.64/26 permit
|
|
||||||
65.54.121.120/29 permit
|
65.54.121.120/29 permit
|
||||||
65.54.190.0/24 permit
|
|
||||||
65.54.241.0/24 permit
|
|
||||||
65.55.29.77 permit
|
65.55.29.77 permit
|
||||||
65.55.33.64/28 permit
|
65.55.33.64/28 permit
|
||||||
65.55.34.0/24 permit
|
|
||||||
65.55.42.224/28 permit
|
65.55.42.224/28 permit
|
||||||
65.55.52.224/27 permit
|
65.55.52.224/27 permit
|
||||||
65.55.78.128/25 permit
|
65.55.78.128/25 permit
|
||||||
65.55.81.48/28 permit
|
65.55.81.48/28 permit
|
||||||
65.55.90.0/24 permit
|
|
||||||
65.55.94.0/25 permit
|
65.55.94.0/25 permit
|
||||||
65.55.111.0/24 permit
|
|
||||||
65.55.113.64/26 permit
|
65.55.113.64/26 permit
|
||||||
65.55.116.0/25 permit
|
|
||||||
65.55.126.0/25 permit
|
65.55.126.0/25 permit
|
||||||
65.55.174.0/25 permit
|
65.55.174.0/25 permit
|
||||||
65.55.178.128/27 permit
|
65.55.178.128/27 permit
|
||||||
@@ -344,7 +416,6 @@
|
|||||||
65.212.180.36 permit
|
65.212.180.36 permit
|
||||||
66.102.0.0/20 permit
|
66.102.0.0/20 permit
|
||||||
66.119.150.192/26 permit
|
66.119.150.192/26 permit
|
||||||
66.162.193.226/31 permit
|
|
||||||
66.163.184.0/24 permit
|
66.163.184.0/24 permit
|
||||||
66.163.185.0/24 permit
|
66.163.185.0/24 permit
|
||||||
66.163.186.0/24 permit
|
66.163.186.0/24 permit
|
||||||
@@ -550,13 +621,11 @@
|
|||||||
74.86.241.250/31 permit
|
74.86.241.250/31 permit
|
||||||
74.112.67.243 permit
|
74.112.67.243 permit
|
||||||
74.125.0.0/16 permit
|
74.125.0.0/16 permit
|
||||||
74.202.227.40 permit
|
|
||||||
74.208.4.200 permit
|
74.208.4.200 permit
|
||||||
74.208.4.201 permit
|
74.208.4.201 permit
|
||||||
74.208.4.220 permit
|
74.208.4.220 permit
|
||||||
74.208.4.221 permit
|
74.208.4.221 permit
|
||||||
74.209.250.0/24 permit
|
74.209.250.0/24 permit
|
||||||
75.2.70.75 permit
|
|
||||||
76.223.128.0/19 permit
|
76.223.128.0/19 permit
|
||||||
76.223.176.0/20 permit
|
76.223.176.0/20 permit
|
||||||
77.238.176.0/24 permit
|
77.238.176.0/24 permit
|
||||||
@@ -579,25 +648,31 @@
|
|||||||
77.238.189.142 permit
|
77.238.189.142 permit
|
||||||
77.238.189.146/31 permit
|
77.238.189.146/31 permit
|
||||||
77.238.189.148/30 permit
|
77.238.189.148/30 permit
|
||||||
|
79.135.106.0/24 permit
|
||||||
|
79.135.107.0/24 permit
|
||||||
|
81.169.146.243 permit
|
||||||
|
81.169.146.245 permit
|
||||||
|
81.169.146.246 permit
|
||||||
81.223.46.0/27 permit
|
81.223.46.0/27 permit
|
||||||
82.165.159.2 permit
|
|
||||||
82.165.159.3 permit
|
|
||||||
82.165.159.4 permit
|
|
||||||
82.165.159.12 permit
|
82.165.159.12 permit
|
||||||
82.165.159.13 permit
|
82.165.159.13 permit
|
||||||
82.165.159.14 permit
|
82.165.159.14 permit
|
||||||
82.165.159.34 permit
|
|
||||||
82.165.159.35 permit
|
|
||||||
82.165.159.40 permit
|
82.165.159.40 permit
|
||||||
82.165.159.41 permit
|
82.165.159.41 permit
|
||||||
82.165.159.42 permit
|
82.165.159.42 permit
|
||||||
82.165.159.45 permit
|
|
||||||
82.165.159.130 permit
|
82.165.159.130 permit
|
||||||
82.165.159.131 permit
|
82.165.159.131 permit
|
||||||
84.116.6.0/23 permit
|
85.9.206.169 permit
|
||||||
84.116.36.0/24 permit
|
85.9.210.45 permit
|
||||||
84.116.50.0/23 permit
|
|
||||||
85.158.136.0/21 permit
|
85.158.136.0/21 permit
|
||||||
|
85.215.255.39 permit
|
||||||
|
85.215.255.40 permit
|
||||||
|
85.215.255.41 permit
|
||||||
|
85.215.255.45 permit
|
||||||
|
85.215.255.46 permit
|
||||||
|
85.215.255.47 permit
|
||||||
|
85.215.255.48 permit
|
||||||
|
85.215.255.49 permit
|
||||||
86.61.88.25 permit
|
86.61.88.25 permit
|
||||||
87.238.80.0/21 permit
|
87.238.80.0/21 permit
|
||||||
87.248.103.12 permit
|
87.248.103.12 permit
|
||||||
@@ -637,12 +712,13 @@
|
|||||||
87.248.117.205 permit
|
87.248.117.205 permit
|
||||||
87.253.232.0/21 permit
|
87.253.232.0/21 permit
|
||||||
89.22.108.0/24 permit
|
89.22.108.0/24 permit
|
||||||
|
91.198.2.0/24 permit
|
||||||
91.211.240.0/22 permit
|
91.211.240.0/22 permit
|
||||||
94.169.2.0/23 permit
|
|
||||||
94.236.119.0/26 permit
|
94.236.119.0/26 permit
|
||||||
94.245.112.0/27 permit
|
94.245.112.0/27 permit
|
||||||
94.245.112.10/31 permit
|
94.245.112.10/31 permit
|
||||||
95.131.104.0/21 permit
|
95.131.104.0/21 permit
|
||||||
|
95.217.114.154 permit
|
||||||
96.43.144.0/20 permit
|
96.43.144.0/20 permit
|
||||||
96.43.144.64/28 permit
|
96.43.144.64/28 permit
|
||||||
96.43.144.64/31 permit
|
96.43.144.64/31 permit
|
||||||
@@ -1133,7 +1209,6 @@
|
|||||||
98.139.245.208/30 permit
|
98.139.245.208/30 permit
|
||||||
98.139.245.212/31 permit
|
98.139.245.212/31 permit
|
||||||
99.78.197.208/28 permit
|
99.78.197.208/28 permit
|
||||||
99.83.190.102 permit
|
|
||||||
103.9.96.0/22 permit
|
103.9.96.0/22 permit
|
||||||
103.28.42.0/24 permit
|
103.28.42.0/24 permit
|
||||||
103.151.192.0/23 permit
|
103.151.192.0/23 permit
|
||||||
@@ -1142,9 +1217,6 @@
|
|||||||
104.43.243.237 permit
|
104.43.243.237 permit
|
||||||
104.44.112.128/25 permit
|
104.44.112.128/25 permit
|
||||||
104.47.0.0/17 permit
|
104.47.0.0/17 permit
|
||||||
104.47.20.0/23 permit
|
|
||||||
104.47.75.0/24 permit
|
|
||||||
104.47.108.0/23 permit
|
|
||||||
104.130.96.0/28 permit
|
104.130.96.0/28 permit
|
||||||
104.130.122.0/23 permit
|
104.130.122.0/23 permit
|
||||||
106.10.144.64/27 permit
|
106.10.144.64/27 permit
|
||||||
@@ -1270,6 +1342,7 @@
|
|||||||
106.50.16.0/28 permit
|
106.50.16.0/28 permit
|
||||||
107.20.18.111 permit
|
107.20.18.111 permit
|
||||||
107.20.210.250 permit
|
107.20.210.250 permit
|
||||||
|
107.22.191.150 permit
|
||||||
108.174.0.0/24 permit
|
108.174.0.0/24 permit
|
||||||
108.174.0.215 permit
|
108.174.0.215 permit
|
||||||
108.174.3.0/24 permit
|
108.174.3.0/24 permit
|
||||||
@@ -1278,9 +1351,8 @@
|
|||||||
108.174.6.215 permit
|
108.174.6.215 permit
|
||||||
108.175.18.45 permit
|
108.175.18.45 permit
|
||||||
108.175.30.45 permit
|
108.175.30.45 permit
|
||||||
108.177.8.0/21 permit
|
|
||||||
108.177.96.0/19 permit
|
|
||||||
108.179.144.0/20 permit
|
108.179.144.0/20 permit
|
||||||
|
109.224.244.0/24 permit
|
||||||
109.237.142.0/24 permit
|
109.237.142.0/24 permit
|
||||||
111.221.23.128/25 permit
|
111.221.23.128/25 permit
|
||||||
111.221.26.0/27 permit
|
111.221.26.0/27 permit
|
||||||
@@ -1331,6 +1403,7 @@
|
|||||||
128.245.248.0/21 permit
|
128.245.248.0/21 permit
|
||||||
129.41.77.70 permit
|
129.41.77.70 permit
|
||||||
129.41.169.249 permit
|
129.41.169.249 permit
|
||||||
|
129.77.16.0/20 permit
|
||||||
129.80.5.164 permit
|
129.80.5.164 permit
|
||||||
129.80.64.36 permit
|
129.80.64.36 permit
|
||||||
129.80.67.121 permit
|
129.80.67.121 permit
|
||||||
@@ -1352,7 +1425,6 @@
|
|||||||
129.213.195.191 permit
|
129.213.195.191 permit
|
||||||
130.61.9.72 permit
|
130.61.9.72 permit
|
||||||
130.162.39.83 permit
|
130.162.39.83 permit
|
||||||
130.211.0.0/22 permit
|
|
||||||
130.248.172.0/24 permit
|
130.248.172.0/24 permit
|
||||||
130.248.173.0/24 permit
|
130.248.173.0/24 permit
|
||||||
131.253.30.0/24 permit
|
131.253.30.0/24 permit
|
||||||
@@ -1361,12 +1433,25 @@
|
|||||||
132.226.26.225 permit
|
132.226.26.225 permit
|
||||||
132.226.49.32 permit
|
132.226.49.32 permit
|
||||||
132.226.56.24 permit
|
132.226.56.24 permit
|
||||||
|
134.128.64.0/19 permit
|
||||||
|
134.128.96.0/19 permit
|
||||||
134.170.27.8 permit
|
134.170.27.8 permit
|
||||||
134.170.113.0/26 permit
|
134.170.113.0/26 permit
|
||||||
134.170.141.64/26 permit
|
134.170.141.64/26 permit
|
||||||
134.170.143.0/24 permit
|
134.170.143.0/24 permit
|
||||||
134.170.174.0/24 permit
|
134.170.174.0/24 permit
|
||||||
135.84.216.0/22 permit
|
135.84.216.0/22 permit
|
||||||
|
136.143.160.0/24 permit
|
||||||
|
136.143.161.0/24 permit
|
||||||
|
136.143.162.0/24 permit
|
||||||
|
136.143.176.0/24 permit
|
||||||
|
136.143.177.0/24 permit
|
||||||
|
136.143.178.49 permit
|
||||||
|
136.143.182.0/23 permit
|
||||||
|
136.143.184.0/24 permit
|
||||||
|
136.143.188.0/24 permit
|
||||||
|
136.143.190.0/23 permit
|
||||||
|
136.146.128.0/20 permit
|
||||||
136.147.128.0/20 permit
|
136.147.128.0/20 permit
|
||||||
136.147.135.0/24 permit
|
136.147.135.0/24 permit
|
||||||
136.147.176.0/20 permit
|
136.147.176.0/20 permit
|
||||||
@@ -1415,6 +1500,7 @@
|
|||||||
146.20.215.0/24 permit
|
146.20.215.0/24 permit
|
||||||
146.20.215.182 permit
|
146.20.215.182 permit
|
||||||
146.88.28.0/24 permit
|
146.88.28.0/24 permit
|
||||||
|
146.148.116.76 permit
|
||||||
147.154.32.0/25 permit
|
147.154.32.0/25 permit
|
||||||
147.243.1.47 permit
|
147.243.1.47 permit
|
||||||
147.243.1.48 permit
|
147.243.1.48 permit
|
||||||
@@ -1424,7 +1510,7 @@
|
|||||||
148.105.0.0/16 permit
|
148.105.0.0/16 permit
|
||||||
148.105.8.0/21 permit
|
148.105.8.0/21 permit
|
||||||
149.72.0.0/16 permit
|
149.72.0.0/16 permit
|
||||||
149.72.223.204 permit
|
149.72.234.184 permit
|
||||||
149.72.248.236 permit
|
149.72.248.236 permit
|
||||||
149.97.173.180 permit
|
149.97.173.180 permit
|
||||||
150.230.98.160 permit
|
150.230.98.160 permit
|
||||||
@@ -1436,9 +1522,6 @@
|
|||||||
155.248.220.138 permit
|
155.248.220.138 permit
|
||||||
155.248.234.149 permit
|
155.248.234.149 permit
|
||||||
155.248.237.141 permit
|
155.248.237.141 permit
|
||||||
157.55.0.192/26 permit
|
|
||||||
157.55.1.128/26 permit
|
|
||||||
157.55.2.0/25 permit
|
|
||||||
157.55.9.128/25 permit
|
157.55.9.128/25 permit
|
||||||
157.55.11.0/25 permit
|
157.55.11.0/25 permit
|
||||||
157.55.49.0/25 permit
|
157.55.49.0/25 permit
|
||||||
@@ -1480,6 +1563,7 @@
|
|||||||
159.183.0.0/16 permit
|
159.183.0.0/16 permit
|
||||||
159.183.68.71 permit
|
159.183.68.71 permit
|
||||||
159.183.79.38 permit
|
159.183.79.38 permit
|
||||||
|
159.183.129.172 permit
|
||||||
160.1.62.192 permit
|
160.1.62.192 permit
|
||||||
161.38.192.0/20 permit
|
161.38.192.0/20 permit
|
||||||
161.38.204.0/22 permit
|
161.38.204.0/22 permit
|
||||||
@@ -1496,7 +1580,10 @@
|
|||||||
163.114.132.120 permit
|
163.114.132.120 permit
|
||||||
163.114.134.16 permit
|
163.114.134.16 permit
|
||||||
163.114.135.16 permit
|
163.114.135.16 permit
|
||||||
|
163.116.128.0/17 permit
|
||||||
|
163.192.116.87 permit
|
||||||
164.152.23.32 permit
|
164.152.23.32 permit
|
||||||
|
164.152.25.241 permit
|
||||||
164.177.132.168/30 permit
|
164.177.132.168/30 permit
|
||||||
166.78.68.0/22 permit
|
166.78.68.0/22 permit
|
||||||
166.78.68.221 permit
|
166.78.68.221 permit
|
||||||
@@ -1522,6 +1609,7 @@
|
|||||||
168.138.5.36 permit
|
168.138.5.36 permit
|
||||||
168.138.73.51 permit
|
168.138.73.51 permit
|
||||||
168.138.77.31 permit
|
168.138.77.31 permit
|
||||||
|
168.138.237.153 permit
|
||||||
168.245.0.0/17 permit
|
168.245.0.0/17 permit
|
||||||
168.245.12.252 permit
|
168.245.12.252 permit
|
||||||
168.245.46.9 permit
|
168.245.46.9 permit
|
||||||
@@ -1531,17 +1619,11 @@
|
|||||||
170.10.132.56/29 permit
|
170.10.132.56/29 permit
|
||||||
170.10.132.64/29 permit
|
170.10.132.64/29 permit
|
||||||
170.10.133.0/24 permit
|
170.10.133.0/24 permit
|
||||||
172.217.0.0/19 permit
|
|
||||||
172.217.32.0/20 permit
|
|
||||||
172.217.128.0/19 permit
|
|
||||||
172.217.160.0/20 permit
|
|
||||||
172.217.192.0/19 permit
|
|
||||||
172.253.56.0/21 permit
|
|
||||||
172.253.112.0/20 permit
|
|
||||||
173.0.84.0/29 permit
|
173.0.84.0/29 permit
|
||||||
173.0.84.224/27 permit
|
173.0.84.224/27 permit
|
||||||
173.0.94.244/30 permit
|
173.0.94.244/30 permit
|
||||||
173.194.0.0/16 permit
|
173.194.0.0/16 permit
|
||||||
|
173.194.0.0/17 permit
|
||||||
173.203.79.182 permit
|
173.203.79.182 permit
|
||||||
173.203.81.39 permit
|
173.203.81.39 permit
|
||||||
173.224.161.128/25 permit
|
173.224.161.128/25 permit
|
||||||
@@ -1565,9 +1647,14 @@
|
|||||||
182.50.78.64/28 permit
|
182.50.78.64/28 permit
|
||||||
183.240.219.64/29 permit
|
183.240.219.64/29 permit
|
||||||
185.4.120.0/22 permit
|
185.4.120.0/22 permit
|
||||||
|
185.11.253.128/27 permit
|
||||||
|
185.11.255.0/24 permit
|
||||||
185.12.80.0/22 permit
|
185.12.80.0/22 permit
|
||||||
185.28.196.0/22 permit
|
185.28.196.0/22 permit
|
||||||
185.58.84.93 permit
|
185.58.84.93 permit
|
||||||
|
185.70.40.0/24 permit
|
||||||
|
185.70.41.0/24 permit
|
||||||
|
185.70.43.0/24 permit
|
||||||
185.80.93.204 permit
|
185.80.93.204 permit
|
||||||
185.80.93.227 permit
|
185.80.93.227 permit
|
||||||
185.80.95.31 permit
|
185.80.95.31 permit
|
||||||
@@ -1575,6 +1662,8 @@
|
|||||||
185.138.56.128/25 permit
|
185.138.56.128/25 permit
|
||||||
185.189.236.0/22 permit
|
185.189.236.0/22 permit
|
||||||
185.211.120.0/22 permit
|
185.211.120.0/22 permit
|
||||||
|
185.233.188.0/23 permit
|
||||||
|
185.233.190.0/23 permit
|
||||||
185.250.236.0/22 permit
|
185.250.236.0/22 permit
|
||||||
185.250.239.148 permit
|
185.250.239.148 permit
|
||||||
185.250.239.168 permit
|
185.250.239.168 permit
|
||||||
@@ -1627,6 +1716,7 @@
|
|||||||
188.125.85.234/31 permit
|
188.125.85.234/31 permit
|
||||||
188.125.85.236/31 permit
|
188.125.85.236/31 permit
|
||||||
188.125.85.238 permit
|
188.125.85.238 permit
|
||||||
|
188.165.51.139 permit
|
||||||
188.172.128.0/20 permit
|
188.172.128.0/20 permit
|
||||||
192.0.64.0/18 permit
|
192.0.64.0/18 permit
|
||||||
192.18.139.154 permit
|
192.18.139.154 permit
|
||||||
@@ -1649,10 +1739,31 @@
|
|||||||
193.109.254.0/23 permit
|
193.109.254.0/23 permit
|
||||||
193.122.128.100 permit
|
193.122.128.100 permit
|
||||||
193.123.56.63 permit
|
193.123.56.63 permit
|
||||||
|
193.142.157.0/24 permit
|
||||||
|
193.142.157.191 permit
|
||||||
|
193.142.157.198 permit
|
||||||
194.19.134.0/25 permit
|
194.19.134.0/25 permit
|
||||||
|
194.25.134.16/28 permit
|
||||||
|
194.25.134.80/28 permit
|
||||||
194.64.234.129 permit
|
194.64.234.129 permit
|
||||||
|
194.97.196.0/24 permit
|
||||||
|
194.97.196.3 permit
|
||||||
|
194.97.196.4 permit
|
||||||
|
194.97.196.11 permit
|
||||||
|
194.97.196.12 permit
|
||||||
|
194.97.204.0/24 permit
|
||||||
|
194.97.204.3 permit
|
||||||
|
194.97.204.4 permit
|
||||||
|
194.97.204.11 permit
|
||||||
|
194.97.204.12 permit
|
||||||
|
194.97.212.0/24 permit
|
||||||
|
194.97.212.3 permit
|
||||||
|
194.97.212.4 permit
|
||||||
|
194.97.212.11 permit
|
||||||
|
194.97.212.12 permit
|
||||||
194.106.220.0/23 permit
|
194.106.220.0/23 permit
|
||||||
194.113.24.0/22 permit
|
194.113.24.0/22 permit
|
||||||
|
194.113.42.0/26 permit
|
||||||
194.154.193.192/27 permit
|
194.154.193.192/27 permit
|
||||||
195.4.92.0/23 permit
|
195.4.92.0/23 permit
|
||||||
195.54.172.0/23 permit
|
195.54.172.0/23 permit
|
||||||
@@ -1666,7 +1777,16 @@
|
|||||||
198.61.254.21 permit
|
198.61.254.21 permit
|
||||||
198.61.254.231 permit
|
198.61.254.231 permit
|
||||||
198.178.234.57 permit
|
198.178.234.57 permit
|
||||||
|
198.202.211.1 permit
|
||||||
198.244.48.0/20 permit
|
198.244.48.0/20 permit
|
||||||
|
198.244.56.107 permit
|
||||||
|
198.244.56.108 permit
|
||||||
|
198.244.56.109 permit
|
||||||
|
198.244.56.111 permit
|
||||||
|
198.244.56.112 permit
|
||||||
|
198.244.56.113 permit
|
||||||
|
198.244.56.114 permit
|
||||||
|
198.244.56.115 permit
|
||||||
198.244.59.30 permit
|
198.244.59.30 permit
|
||||||
198.244.59.33 permit
|
198.244.59.33 permit
|
||||||
198.244.59.35 permit
|
198.244.59.35 permit
|
||||||
@@ -1736,9 +1856,11 @@
|
|||||||
204.92.114.187 permit
|
204.92.114.187 permit
|
||||||
204.92.114.203 permit
|
204.92.114.203 permit
|
||||||
204.92.114.204/31 permit
|
204.92.114.204/31 permit
|
||||||
|
204.216.164.202 permit
|
||||||
204.220.160.0/21 permit
|
204.220.160.0/21 permit
|
||||||
204.220.168.0/21 permit
|
204.220.168.0/21 permit
|
||||||
204.220.176.0/20 permit
|
204.220.176.0/20 permit
|
||||||
|
204.220.181.105 permit
|
||||||
204.232.168.0/24 permit
|
204.232.168.0/24 permit
|
||||||
205.139.110.0/24 permit
|
205.139.110.0/24 permit
|
||||||
205.201.128.0/20 permit
|
205.201.128.0/20 permit
|
||||||
@@ -1766,7 +1888,6 @@
|
|||||||
207.46.52.79 permit
|
207.46.52.79 permit
|
||||||
207.46.58.128/25 permit
|
207.46.58.128/25 permit
|
||||||
207.46.116.128/29 permit
|
207.46.116.128/29 permit
|
||||||
207.46.117.0/24 permit
|
|
||||||
207.46.132.128/27 permit
|
207.46.132.128/27 permit
|
||||||
207.46.198.0/25 permit
|
207.46.198.0/25 permit
|
||||||
207.46.200.0/27 permit
|
207.46.200.0/27 permit
|
||||||
@@ -1813,8 +1934,6 @@
|
|||||||
208.71.42.212/31 permit
|
208.71.42.212/31 permit
|
||||||
208.71.42.214 permit
|
208.71.42.214 permit
|
||||||
208.72.249.240/29 permit
|
208.72.249.240/29 permit
|
||||||
208.74.204.5 permit
|
|
||||||
208.74.204.9 permit
|
|
||||||
208.75.120.0/22 permit
|
208.75.120.0/22 permit
|
||||||
208.76.62.0/24 permit
|
208.76.62.0/24 permit
|
||||||
208.76.63.0/24 permit
|
208.76.63.0/24 permit
|
||||||
@@ -1874,17 +1993,11 @@
|
|||||||
212.82.111.228/31 permit
|
212.82.111.228/31 permit
|
||||||
212.82.111.230 permit
|
212.82.111.230 permit
|
||||||
212.123.28.40 permit
|
212.123.28.40 permit
|
||||||
212.227.15.3 permit
|
212.227.15.7 permit
|
||||||
212.227.15.4 permit
|
212.227.15.8 permit
|
||||||
212.227.15.5 permit
|
|
||||||
212.227.15.6 permit
|
|
||||||
212.227.15.14 permit
|
|
||||||
212.227.15.15 permit
|
212.227.15.15 permit
|
||||||
212.227.15.18 permit
|
212.227.15.18 permit
|
||||||
212.227.15.19 permit
|
212.227.15.19 permit
|
||||||
212.227.15.25 permit
|
|
||||||
212.227.15.26 permit
|
|
||||||
212.227.15.29 permit
|
|
||||||
212.227.15.44 permit
|
212.227.15.44 permit
|
||||||
212.227.15.45 permit
|
212.227.15.45 permit
|
||||||
212.227.15.46 permit
|
212.227.15.46 permit
|
||||||
@@ -1892,23 +2005,32 @@
|
|||||||
212.227.15.50 permit
|
212.227.15.50 permit
|
||||||
212.227.15.52 permit
|
212.227.15.52 permit
|
||||||
212.227.15.53 permit
|
212.227.15.53 permit
|
||||||
212.227.15.54 permit
|
212.227.17.1 permit
|
||||||
212.227.15.55 permit
|
212.227.17.2 permit
|
||||||
212.227.17.11 permit
|
212.227.17.7 permit
|
||||||
212.227.17.12 permit
|
212.227.17.16 permit
|
||||||
212.227.17.18 permit
|
212.227.17.17 permit
|
||||||
212.227.17.19 permit
|
|
||||||
212.227.17.20 permit
|
212.227.17.20 permit
|
||||||
212.227.17.21 permit
|
212.227.17.21 permit
|
||||||
212.227.17.22 permit
|
212.227.17.22 permit
|
||||||
212.227.17.26 permit
|
212.227.17.26 permit
|
||||||
|
212.227.17.27 permit
|
||||||
212.227.17.28 permit
|
212.227.17.28 permit
|
||||||
212.227.17.29 permit
|
212.227.17.29 permit
|
||||||
|
212.227.126.206 permit
|
||||||
|
212.227.126.207 permit
|
||||||
|
212.227.126.208 permit
|
||||||
|
212.227.126.209 permit
|
||||||
|
212.227.126.220 permit
|
||||||
|
212.227.126.221 permit
|
||||||
|
212.227.126.222 permit
|
||||||
|
212.227.126.223 permit
|
||||||
212.227.126.224 permit
|
212.227.126.224 permit
|
||||||
212.227.126.225 permit
|
212.227.126.225 permit
|
||||||
212.227.126.226 permit
|
212.227.126.226 permit
|
||||||
212.227.126.227 permit
|
212.227.126.227 permit
|
||||||
213.46.255.0/24 permit
|
213.95.19.64/27 permit
|
||||||
|
213.95.135.4 permit
|
||||||
213.199.128.139 permit
|
213.199.128.139 permit
|
||||||
213.199.128.145 permit
|
213.199.128.145 permit
|
||||||
213.199.138.181 permit
|
213.199.138.181 permit
|
||||||
@@ -1918,6 +2040,7 @@
|
|||||||
216.17.150.242 permit
|
216.17.150.242 permit
|
||||||
216.17.150.251 permit
|
216.17.150.251 permit
|
||||||
216.24.224.0/20 permit
|
216.24.224.0/20 permit
|
||||||
|
216.27.86.152/31 permit
|
||||||
216.39.60.154/31 permit
|
216.39.60.154/31 permit
|
||||||
216.39.60.156/30 permit
|
216.39.60.156/30 permit
|
||||||
216.39.60.160/30 permit
|
216.39.60.160/30 permit
|
||||||
@@ -1955,6 +2078,8 @@
|
|||||||
216.99.5.68 permit
|
216.99.5.68 permit
|
||||||
216.109.114.32/27 permit
|
216.109.114.32/27 permit
|
||||||
216.109.114.64/29 permit
|
216.109.114.64/29 permit
|
||||||
|
216.113.162.65 permit
|
||||||
|
216.113.163.65 permit
|
||||||
216.128.126.97 permit
|
216.128.126.97 permit
|
||||||
216.136.162.65 permit
|
216.136.162.65 permit
|
||||||
216.136.162.120/29 permit
|
216.136.162.120/29 permit
|
||||||
@@ -1969,8 +2094,6 @@
|
|||||||
216.205.24.0/24 permit
|
216.205.24.0/24 permit
|
||||||
216.221.160.0/19 permit
|
216.221.160.0/19 permit
|
||||||
216.239.32.0/19 permit
|
216.239.32.0/19 permit
|
||||||
217.72.192.77 permit
|
|
||||||
217.72.192.78 permit
|
|
||||||
217.77.141.52 permit
|
217.77.141.52 permit
|
||||||
217.77.141.59 permit
|
217.77.141.59 permit
|
||||||
217.175.194.0/24 permit
|
217.175.194.0/24 permit
|
||||||
@@ -1982,6 +2105,21 @@
|
|||||||
2001:0868:0100:0600::/64 permit
|
2001:0868:0100:0600::/64 permit
|
||||||
2001:4860:4000::/36 permit
|
2001:4860:4000::/36 permit
|
||||||
2001:748:100:40::2:0/112 permit
|
2001:748:100:40::2:0/112 permit
|
||||||
|
2001:748:400:1300::3 permit
|
||||||
|
2001:748:400:1300::4 permit
|
||||||
|
2001:748:400:1301::0/64 permit
|
||||||
|
2001:748:400:1301::3 permit
|
||||||
|
2001:748:400:1301::4 permit
|
||||||
|
2001:748:400:2300::3 permit
|
||||||
|
2001:748:400:2300::4 permit
|
||||||
|
2001:748:400:2301::0/64 permit
|
||||||
|
2001:748:400:2301::3 permit
|
||||||
|
2001:748:400:2301::4 permit
|
||||||
|
2001:748:400:3300::3 permit
|
||||||
|
2001:748:400:3300::4 permit
|
||||||
|
2001:748:400:3301::0/64 permit
|
||||||
|
2001:748:400:3301::3 permit
|
||||||
|
2001:748:400:3301::4 permit
|
||||||
2404:6800:4000::/36 permit
|
2404:6800:4000::/36 permit
|
||||||
2603:1010:3:3::5b permit
|
2603:1010:3:3::5b permit
|
||||||
2603:1020:201:10::10f permit
|
2603:1020:201:10::10f permit
|
||||||
@@ -2001,4 +2139,5 @@
|
|||||||
2620:119:50c0:207::/64 permit
|
2620:119:50c0:207::/64 permit
|
||||||
2620:119:50c0:207::215 permit
|
2620:119:50c0:207::215 permit
|
||||||
2800:3f0:4000::/36 permit
|
2800:3f0:4000::/36 permit
|
||||||
194.25.134.0/24 permit # t-online.de
|
49.12.4.251 permit # checks.mailcow.email
|
||||||
|
2a01:4f8:c17:7906::10 permit # checks.mailcow.email
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
/.+\.guru$/i
|
/.+\.guru$/i
|
||||||
/.+\.icu$/i
|
/.+\.icu$/i
|
||||||
/.+\.id$/i
|
/.+\.id$/i
|
||||||
/.+\.info$/i
|
|
||||||
/.+\.in.net$/i
|
/.+\.in.net$/i
|
||||||
/.+\.ir$/i
|
/.+\.ir$/i
|
||||||
/.+\.jetzt$/i
|
/.+\.jetzt$/i
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ try {
|
|||||||
error_log("ALIAS EXPANDER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
error_log("ALIAS EXPANDER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
||||||
$goto_branch_array = explode(',', $goto_branch);
|
$goto_branch_array = explode(',', $goto_branch);
|
||||||
} else {
|
} else {
|
||||||
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` AND '1'");
|
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` = '1'");
|
||||||
$stmt->execute(array(':domain' => $parsed_goto['domain']));
|
$stmt->execute(array(':domain' => $parsed_goto['domain']));
|
||||||
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
|
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
|
||||||
if ($goto_branch) {
|
if ($goto_branch) {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ function normalize_email($email) {
|
|||||||
$email = explode('@', $email);
|
$email = explode('@', $email);
|
||||||
$email[0] = str_replace('.', '', $email[0]);
|
$email[0] = str_replace('.', '', $email[0]);
|
||||||
$email = implode('@', $email);
|
$email = implode('@', $email);
|
||||||
}
|
}
|
||||||
$gm_alt = "@googlemail.com";
|
$gm_alt = "@googlemail.com";
|
||||||
if (substr_compare($email, $gm_alt, -strlen($gm_alt)) == 0) {
|
if (substr_compare($email, $gm_alt, -strlen($gm_alt)) == 0) {
|
||||||
$email = explode('@', $email);
|
$email = explode('@', $email);
|
||||||
@@ -114,7 +114,7 @@ function ucl_rcpts($object, $type) {
|
|||||||
$rcpt[] = str_replace('/', '\/', $row['address']);
|
$rcpt[] = str_replace('/', '\/', $row['address']);
|
||||||
}
|
}
|
||||||
// Aliases by alias domains
|
// Aliases by alias domains
|
||||||
$stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `alias` FROM `mailbox`
|
$stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `alias` FROM `mailbox`
|
||||||
LEFT OUTER JOIN `alias_domain` ON `mailbox`.`domain` = `alias_domain`.`target_domain`
|
LEFT OUTER JOIN `alias_domain` ON `mailbox`.`domain` = `alias_domain`.`target_domain`
|
||||||
WHERE `mailbox`.`username` = :object");
|
WHERE `mailbox`.`username` = :object");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
@@ -184,7 +184,7 @@ while ($row = array_shift($rows)) {
|
|||||||
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
|
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("SELECT `option`, `value` FROM `filterconf`
|
$stmt = $pdo->prepare("SELECT `option`, `value` FROM `filterconf`
|
||||||
WHERE (`option` = 'highspamlevel' OR `option` = 'lowspamlevel')
|
WHERE (`option` = 'highspamlevel' OR `option` = 'lowspamlevel')
|
||||||
AND `object`= :object");
|
AND `object`= :object");
|
||||||
$stmt->execute(array(':object' => $row['object']));
|
$stmt->execute(array(':object' => $row['object']));
|
||||||
@@ -468,4 +468,36 @@ while ($row = array_shift($rows)) {
|
|||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Start internal aliases
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SELECT `id`, `address`, `domain` FROM `alias` WHERE `active` = '1' AND `internal` = '1'");
|
||||||
|
$aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while ($alias = array_shift($aliases)) {
|
||||||
|
// build allowed_domains regex and add target domain and alias domains
|
||||||
|
$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `active` = '1' AND `target_domain` = :target_domain");
|
||||||
|
$stmt->execute(array(':target_domain' => $alias['domain']));
|
||||||
|
$allowed_domains = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$allowed_domains = array_map(function($item) {
|
||||||
|
return str_replace('.', '\.', $item['alias_domain']);
|
||||||
|
}, $allowed_domains);
|
||||||
|
$allowed_domains[] = str_replace('.', '\.', $alias['domain']);
|
||||||
|
$allowed_domains = implode('|', $allowed_domains);
|
||||||
|
?>
|
||||||
|
internal_alias_<?=$alias['id'];?> {
|
||||||
|
priority = 10;
|
||||||
|
rcpt = "<?=$alias['address'];?>";
|
||||||
|
from = "/^((?!.*@(<?=$allowed_domains;?>)).)*$/";
|
||||||
|
apply "default" {
|
||||||
|
MAILCOW_INTERNAL_ALIAS = 9999.0;
|
||||||
|
}
|
||||||
|
symbols [
|
||||||
|
"MAILCOW_INTERNAL_ALIAS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = "FREEMAIL_FROM & !DMARC_POLICY_ALLOW & !MAILLIST& !WHITELISTED_FWD_HOST & -g+:policies";
|
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
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
oletools {
|
|
||||||
# default olefy settings
|
|
||||||
servers = "olefy:10055";
|
|
||||||
# needs to be set explicitly for Rspamd < 1.9.5
|
|
||||||
scan_mime_parts = true;
|
|
||||||
# mime-part regex matching in content-type or filename
|
|
||||||
# block all macros
|
|
||||||
extended = true;
|
|
||||||
max_size = 3145728;
|
|
||||||
timeout = 20.0;
|
|
||||||
retransmits = 1;
|
|
||||||
}
|
|
||||||
@@ -102,7 +102,7 @@ rspamd_config:register_symbol({
|
|||||||
local rcpt_split = rspamd_str_split(rcpt['addr'], '@')
|
local rcpt_split = rspamd_str_split(rcpt['addr'], '@')
|
||||||
if #rcpt_split == 2 then
|
if #rcpt_split == 2 then
|
||||||
if rcpt_split[1] == 'postmaster' then
|
if rcpt_split[1] == 'postmaster' then
|
||||||
task:set_pre_result('accept', 'whitelisting postmaster smtp rcpt')
|
task:set_pre_result('accept', 'whitelisting postmaster smtp rcpt', 'postmaster')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -167,7 +167,7 @@ rspamd_config:register_symbol({
|
|||||||
for k,v in pairs(data) do
|
for k,v in pairs(data) do
|
||||||
if (v and v ~= userdata and v == '1') then
|
if (v and v ~= userdata and v == '1') then
|
||||||
rspamd_logger.infox(rspamd_config, "found ip in keep_spam map, setting pre-result")
|
rspamd_logger.infox(rspamd_config, "found ip in keep_spam map, setting pre-result")
|
||||||
task:set_pre_result('accept', 'ip matched with forward hosts')
|
task:set_pre_result('accept', 'ip matched with forward hosts', 'keep_spam')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -454,12 +454,18 @@ rspamd_config:register_symbol({
|
|||||||
local redis_params = rspamd_parse_redis_server('dyn_rl')
|
local redis_params = rspamd_parse_redis_server('dyn_rl')
|
||||||
local rspamd_logger = require "rspamd_logger"
|
local rspamd_logger = require "rspamd_logger"
|
||||||
local envfrom = task:get_from(1)
|
local envfrom = task:get_from(1)
|
||||||
|
local envrcpt = task:get_recipients(1) or {}
|
||||||
local uname = task:get_user()
|
local uname = task:get_user()
|
||||||
if not envfrom or not uname then
|
if not envfrom or not uname then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local uname = uname:lower()
|
local uname = uname:lower()
|
||||||
|
|
||||||
|
if #envrcpt == 1 and envrcpt[1].addr:lower() == uname then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
local env_from_domain = envfrom[1].domain:lower() -- get smtp from domain in lower case
|
local env_from_domain = envfrom[1].domain:lower() -- get smtp from domain in lower case
|
||||||
|
|
||||||
local function redis_cb_user(err, data)
|
local function redis_cb_user(err, data)
|
||||||
@@ -544,13 +550,13 @@ rspamd_config:register_symbol({
|
|||||||
-- determine newline type
|
-- determine newline type
|
||||||
local function newline(task)
|
local function newline(task)
|
||||||
local t = task:get_newlines_type()
|
local t = task:get_newlines_type()
|
||||||
|
|
||||||
if t == 'cr' then
|
if t == 'cr' then
|
||||||
return '\r'
|
return '\r'
|
||||||
elseif t == 'lf' then
|
elseif t == 'lf' then
|
||||||
return '\n'
|
return '\n'
|
||||||
end
|
end
|
||||||
|
|
||||||
return '\r\n'
|
return '\r\n'
|
||||||
end
|
end
|
||||||
-- retrieve footer
|
-- retrieve footer
|
||||||
@@ -558,7 +564,7 @@ rspamd_config:register_symbol({
|
|||||||
if err or type(data) ~= 'string' then
|
if err or type(data) ~= 'string' then
|
||||||
rspamd_logger.infox(rspamd_config, "domain wide footer request for user %s returned invalid or empty data (\"%s\") or error (\"%s\")", uname, data, err)
|
rspamd_logger.infox(rspamd_config, "domain wide footer request for user %s returned invalid or empty data (\"%s\") or error (\"%s\")", uname, data, err)
|
||||||
else
|
else
|
||||||
|
|
||||||
-- parse json string
|
-- parse json string
|
||||||
local footer = cjson.decode(data)
|
local footer = cjson.decode(data)
|
||||||
if not footer then
|
if not footer then
|
||||||
@@ -607,26 +613,30 @@ rspamd_config:register_symbol({
|
|||||||
if footer.plain and footer.plain ~= "" then
|
if footer.plain and footer.plain ~= "" then
|
||||||
footer.plain = lua_util.jinja_template(footer.plain, replacements, true)
|
footer.plain = lua_util.jinja_template(footer.plain, replacements, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add footer
|
-- add footer
|
||||||
local out = {}
|
local out = {}
|
||||||
local rewrite = lua_mime.add_text_footer(task, footer.html, footer.plain) or {}
|
local rewrite = lua_mime.add_text_footer(task, footer.html, footer.plain) or {}
|
||||||
|
|
||||||
local seen_cte
|
local seen_cte
|
||||||
local newline_s = newline(task)
|
local newline_s = newline(task)
|
||||||
|
|
||||||
local function rewrite_ct_cb(name, hdr)
|
local function rewrite_ct_cb(name, hdr)
|
||||||
if rewrite.need_rewrite_ct then
|
if rewrite.need_rewrite_ct then
|
||||||
if name:lower() == 'content-type' then
|
if name:lower() == 'content-type' then
|
||||||
local nct = string.format('%s: %s/%s; charset=utf-8',
|
-- include boundary if present
|
||||||
'Content-Type', rewrite.new_ct.type, rewrite.new_ct.subtype)
|
local boundary_part = rewrite.new_ct.boundary and
|
||||||
|
string.format('; boundary="%s"', rewrite.new_ct.boundary) or ''
|
||||||
|
local nct = string.format('%s: %s/%s; charset=utf-8%s',
|
||||||
|
'Content-Type', rewrite.new_ct.type, rewrite.new_ct.subtype, boundary_part)
|
||||||
out[#out + 1] = nct
|
out[#out + 1] = nct
|
||||||
-- update Content-Type header
|
-- update Content-Type header (include boundary if present)
|
||||||
task:set_milter_reply({
|
task:set_milter_reply({
|
||||||
remove_headers = {['Content-Type'] = 0},
|
remove_headers = {['Content-Type'] = 0},
|
||||||
})
|
})
|
||||||
task:set_milter_reply({
|
task:set_milter_reply({
|
||||||
add_headers = {['Content-Type'] = string.format('%s/%s; charset=utf-8', rewrite.new_ct.type, rewrite.new_ct.subtype)}
|
add_headers = {['Content-Type'] = string.format('%s/%s; charset=utf-8%s',
|
||||||
|
rewrite.new_ct.type, rewrite.new_ct.subtype, boundary_part)}
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
elseif name:lower() == 'content-transfer-encoding' then
|
elseif name:lower() == 'content-transfer-encoding' then
|
||||||
@@ -645,16 +655,16 @@ rspamd_config:register_symbol({
|
|||||||
end
|
end
|
||||||
out[#out + 1] = hdr.raw:gsub('\r?\n?$', '')
|
out[#out + 1] = hdr.raw:gsub('\r?\n?$', '')
|
||||||
end
|
end
|
||||||
|
|
||||||
task:headers_foreach(rewrite_ct_cb, {full = true})
|
task:headers_foreach(rewrite_ct_cb, {full = true})
|
||||||
|
|
||||||
if not seen_cte and rewrite.need_rewrite_ct then
|
if not seen_cte and rewrite.need_rewrite_ct then
|
||||||
out[#out + 1] = string.format('%s: %s', 'Content-Transfer-Encoding', 'quoted-printable')
|
out[#out + 1] = string.format('%s: %s', 'Content-Transfer-Encoding', 'quoted-printable')
|
||||||
end
|
end
|
||||||
|
|
||||||
-- End of headers
|
-- End of headers
|
||||||
out[#out + 1] = newline_s
|
out[#out + 1] = newline_s
|
||||||
|
|
||||||
if rewrite.out then
|
if rewrite.out then
|
||||||
for _,o in ipairs(rewrite.out) do
|
for _,o in ipairs(rewrite.out) do
|
||||||
out[#out + 1] = o
|
out[#out + 1] = o
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ foreach (json_decode($rcpts, true) as $rcpt) {
|
|||||||
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
||||||
$goto_branch_array = explode(',', $goto_branch);
|
$goto_branch_array = explode(',', $goto_branch);
|
||||||
} else {
|
} else {
|
||||||
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` AND '1'");
|
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` = '1'");
|
||||||
$stmt->execute(array(':domain' => $parsed_goto['domain']));
|
$stmt->execute(array(':domain' => $parsed_goto['domain']));
|
||||||
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
|
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
|
||||||
if ($goto_branch) {
|
if ($goto_branch) {
|
||||||
@@ -236,6 +236,9 @@ foreach ($rcpt_final_mailboxes as $rcpt_final) {
|
|||||||
':action' => $action,
|
':action' => $action,
|
||||||
':fuzzy_hashes' => $fuzzy
|
':fuzzy_hashes' => $fuzzy
|
||||||
));
|
));
|
||||||
|
$lastId = $pdo->lastInsertId();
|
||||||
|
$stmt_update = $pdo->prepare("UPDATE `quarantine` SET `qhash` = SHA2(CONCAT(`id`, `qid`), 256) WHERE `id` = :id");
|
||||||
|
$stmt_update->execute(array(':id' => $lastId));
|
||||||
$stmt = $pdo->prepare('DELETE FROM `quarantine` WHERE `rcpt` = :rcpt AND `id` NOT IN (
|
$stmt = $pdo->prepare('DELETE FROM `quarantine` WHERE `rcpt` = :rcpt AND `id` NOT IN (
|
||||||
SELECT `id`
|
SELECT `id`
|
||||||
FROM (
|
FROM (
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ foreach (json_decode($rcpts, true) as $rcpt) {
|
|||||||
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
|
||||||
$goto_branch_array = explode(',', $goto_branch);
|
$goto_branch_array = explode(',', $goto_branch);
|
||||||
} else {
|
} else {
|
||||||
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` AND '1'");
|
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` = '1'");
|
||||||
$stmt->execute(array(':domain' => $parsed_goto['domain']));
|
$stmt->execute(array(':domain' => $parsed_goto['domain']));
|
||||||
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
|
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
|
||||||
if ($goto_branch) {
|
if ($goto_branch) {
|
||||||
|
|||||||
@@ -5,6 +5,16 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
window.location.href = '/user';
|
window.location.href = '/user';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// logout function
|
||||||
|
function mc_logout() {
|
||||||
|
fetch("/", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: "logout=1"
|
||||||
|
}).then(() => window.location.href = '/');
|
||||||
|
}
|
||||||
|
|
||||||
// Custom SOGo JS
|
// Custom SOGo JS
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
SOGoFoldersSendEMailNotifications = YES;
|
SOGoFoldersSendEMailNotifications = YES;
|
||||||
SOGoForwardEnabled = YES;
|
SOGoForwardEnabled = YES;
|
||||||
|
|
||||||
|
// Added with SOGo 5.12 - Allows users to cleanup there maildirectories by deleting mails oder than X
|
||||||
|
SOGoEnableMailCleaning = YES;
|
||||||
|
|
||||||
// Fixes "MODIFICATION_FAILED" error (HTTP 412) in Clients when accepting invitations from external services
|
// Fixes "MODIFICATION_FAILED" error (HTTP 412) in Clients when accepting invitations from external services
|
||||||
SOGoDisableOrganizerEventCheck = YES;
|
SOGoDisableOrganizerEventCheck = YES;
|
||||||
|
|
||||||
@@ -83,6 +86,12 @@
|
|||||||
SOGoMaximumFailedLoginInterval = 900;
|
SOGoMaximumFailedLoginInterval = 900;
|
||||||
SOGoFailedLoginBlockInterval = 900;
|
SOGoFailedLoginBlockInterval = 900;
|
||||||
|
|
||||||
|
// Enable SOGo URL Description for GDPR compliance, this may cause some issues with calendars and contacts. Also uncomment the encryption key below to use it.
|
||||||
|
//SOGoURLEncryptionEnabled = NO;
|
||||||
|
|
||||||
|
// Set a 16 character encryption key for SOGo URL Description, change this to your own value
|
||||||
|
//SOGoURLPathEncryptionKey = "SOGoSuperSecret0";
|
||||||
|
|
||||||
GCSChannelCollectionTimer = 60;
|
GCSChannelCollectionTimer = 60;
|
||||||
GCSChannelExpireAge = 60;
|
GCSChannelExpireAge = 60;
|
||||||
|
|
||||||
@@ -91,7 +100,7 @@
|
|||||||
//SoDebugBaseURL = YES;
|
//SoDebugBaseURL = YES;
|
||||||
//ImapDebugEnabled = YES;
|
//ImapDebugEnabled = YES;
|
||||||
//SOGoEASDebugEnabled = YES;
|
//SOGoEASDebugEnabled = YES;
|
||||||
SOGoEASSearchInBody = YES; // Experimental. Enabled since 2023-10
|
SOGoEASSearchInBody = YES;
|
||||||
//LDAPDebugEnabled = YES;
|
//LDAPDebugEnabled = YES;
|
||||||
//PGDebugEnabled = YES;
|
//PGDebugEnabled = YES;
|
||||||
//MySQL4DebugEnabled = YES;
|
//MySQL4DebugEnabled = YES;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ elseif (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] !=
|
|||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php';
|
||||||
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
$clamd_status = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["SKIP_CLAMD"])) ? false : true;
|
$clamd_status = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["SKIP_CLAMD"])) ? false : true;
|
||||||
|
$olefy_status = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["SKIP_OLEFY"])) ? false : true;
|
||||||
|
|
||||||
|
|
||||||
if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CACHE')) {
|
if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CACHE')) {
|
||||||
@@ -33,6 +34,7 @@ $vmail_df = explode(',', (string)json_decode(docker('post', 'dovecot-mailcow', '
|
|||||||
// containers
|
// containers
|
||||||
$containers_info = (array) docker('info');
|
$containers_info = (array) docker('info');
|
||||||
if ($clamd_status === false) unset($containers_info['clamd-mailcow']);
|
if ($clamd_status === false) unset($containers_info['clamd-mailcow']);
|
||||||
|
if ($olefy_status === false) unset($containers_info['olefy-mailcow']);
|
||||||
ksort($containers_info);
|
ksort($containers_info);
|
||||||
$containers = array();
|
$containers = array();
|
||||||
foreach ($containers_info as $container => $container_info) {
|
foreach ($containers_info as $container => $container_info) {
|
||||||
@@ -77,6 +79,7 @@ $template_data = [
|
|||||||
'gal' => @$_SESSION['gal'],
|
'gal' => @$_SESSION['gal'],
|
||||||
'license_guid' => license('guid'),
|
'license_guid' => license('guid'),
|
||||||
'clamd_status' => $clamd_status,
|
'clamd_status' => $clamd_status,
|
||||||
|
'olefy_status' => $olefy_status,
|
||||||
'containers' => $containers,
|
'containers' => $containers,
|
||||||
'ip_check' => customize('get', 'ip_check'),
|
'ip_check' => customize('get', 'ip_check'),
|
||||||
'lang_admin' => json_encode($lang['admin']),
|
'lang_admin' => json_encode($lang['admin']),
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING'];
|
|||||||
|
|
||||||
$template = 'admin_index.twig';
|
$template = 'admin_index.twig';
|
||||||
$template_data = [
|
$template_data = [
|
||||||
'login_delay' => @$_SESSION['ldelay']
|
'login_delay' => @$_SESSION['ldelay'],
|
||||||
|
'custom_login' => customize('get', 'custom_login'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$js_minifier->add('/web/js/site/index.js');
|
$js_minifier->add('/web/js/site/index.js');
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ $template_data = [
|
|||||||
'logo_specs' => customize('get', 'main_logo_specs'),
|
'logo_specs' => customize('get', 'main_logo_specs'),
|
||||||
'logo_dark_specs' => customize('get', 'main_logo_dark_specs'),
|
'logo_dark_specs' => customize('get', 'main_logo_dark_specs'),
|
||||||
'ip_check' => customize('get', 'ip_check'),
|
'ip_check' => customize('get', 'ip_check'),
|
||||||
|
'custom_login' => customize('get', 'custom_login'),
|
||||||
'password_complexity' => password_complexity('get'),
|
'password_complexity' => password_complexity('get'),
|
||||||
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],
|
'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'],
|
||||||
'cors_settings' => $cors_settings,
|
'cors_settings' => $cors_settings,
|
||||||
|
|||||||
@@ -5847,6 +5847,7 @@ paths:
|
|||||||
client_id: "mailcow_client"
|
client_id: "mailcow_client"
|
||||||
client_secret: "*"
|
client_secret: "*"
|
||||||
redirect_url: "https://mail.mailcow.tld"
|
redirect_url: "https://mail.mailcow.tld"
|
||||||
|
redirect_url_extra: ["https://extramail.mailcow.tld"]
|
||||||
version: "26.1.3"
|
version: "26.1.3"
|
||||||
default_template: "Default"
|
default_template: "Default"
|
||||||
mappers:
|
mappers:
|
||||||
@@ -5900,6 +5901,9 @@ paths:
|
|||||||
redirect_url:
|
redirect_url:
|
||||||
description: The redirect URL that OIDC Provider will use after authentication. Required if `authsource` is keycloak or generic-oidc.
|
description: The redirect URL that OIDC Provider will use after authentication. Required if `authsource` is keycloak or generic-oidc.
|
||||||
type: string
|
type: string
|
||||||
|
redirect_url_extra:
|
||||||
|
description: Additional redirect URLs that OIDC Provider can use after authentication if valid.
|
||||||
|
type: array
|
||||||
version:
|
version:
|
||||||
description: Specifies the Keycloak version. Required if `authsource` is keycloak.
|
description: Specifies the Keycloak version. Required if `authsource` is keycloak.
|
||||||
type: string
|
type: string
|
||||||
@@ -5990,6 +5994,7 @@ paths:
|
|||||||
client_id: "mailcow_client"
|
client_id: "mailcow_client"
|
||||||
client_secret: "Xy7GdPqvJ9m3R8sT2LkVZ5W1oNbCaYQf"
|
client_secret: "Xy7GdPqvJ9m3R8sT2LkVZ5W1oNbCaYQf"
|
||||||
redirect_url: "https://mail.mailcow.tld"
|
redirect_url: "https://mail.mailcow.tld"
|
||||||
|
redirect_url_extra: ["https://extramail.mailcow.tld"]
|
||||||
version: "26.1.3"
|
version: "26.1.3"
|
||||||
default_template: "Default"
|
default_template: "Default"
|
||||||
mappers: ["small_mbox", "medium_mbox"]
|
mappers: ["small_mbox", "medium_mbox"]
|
||||||
@@ -6034,6 +6039,7 @@ paths:
|
|||||||
client_id: "mailcow_client"
|
client_id: "mailcow_client"
|
||||||
client_secret: "Xy7GdPqvJ9m3R8sT2LkVZ5W1oNbCaYQf"
|
client_secret: "Xy7GdPqvJ9m3R8sT2LkVZ5W1oNbCaYQf"
|
||||||
redirect_url: "https://mail.mailcow.tld"
|
redirect_url: "https://mail.mailcow.tld"
|
||||||
|
redirect_url_extra: ["https://extramail.mailcow.tld"]
|
||||||
client_scopes: "openid profile email mailcow_template"
|
client_scopes: "openid profile email mailcow_template"
|
||||||
default_template: "Default"
|
default_template: "Default"
|
||||||
mappers: ["small_mbox", "medium_mbox"]
|
mappers: ["small_mbox", "medium_mbox"]
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ if (count($records) == 0 || $records[0]['target'] != '') { ?>
|
|||||||
<authentication>password-cleartext</authentication>
|
<authentication>password-cleartext</authentication>
|
||||||
</outgoingServer>
|
</outgoingServer>
|
||||||
|
|
||||||
<enable visiturl="https://<?=$mailcow_hostname; ?><?php if ($port != 443) echo ':'.$port; ?>/admin.php">
|
<enable visiturl="https://<?=$mailcow_hostname; ?><?php if ($port != 443) echo ':'.$port; ?>/admin">
|
||||||
<instruction>If you didn't change the password given to you by the administrator or if you didn't change it in a long time, please consider doing that now.</instruction>
|
<instruction>If you didn't change the password given to you by the administrator or if you didn't change it in a long time, please consider doing that now.</instruction>
|
||||||
<instruction lang="de">Sollten Sie das Ihnen durch den Administrator vergebene Passwort noch nicht geändert haben, empfehlen wir dies nun zu tun. Auch ein altes Passwort sollte aus Sicherheitsgründen geändert werden.</instruction>
|
<instruction lang="de">Sollten Sie das Ihnen durch den Administrator vergebene Passwort noch nicht geändert haben, empfehlen wir dies nun zu tun. Auch ein altes Passwort sollte aus Sicherheitsgründen geändert werden.</instruction>
|
||||||
</enable>
|
</enable>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ if(file_exists('inc/vars.local.inc.php')) {
|
|||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.auth.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.auth.inc.php';
|
||||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
|
||||||
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.ratelimit.inc.php';
|
||||||
$default_autodiscover_config = $autodiscover_config;
|
$default_autodiscover_config = $autodiscover_config;
|
||||||
$autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config);
|
$autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config);
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING'];
|
|||||||
$template = 'domainadmin_index.twig';
|
$template = 'domainadmin_index.twig';
|
||||||
$template_data = [
|
$template_data = [
|
||||||
'login_delay' => @$_SESSION['ldelay'],
|
'login_delay' => @$_SESSION['ldelay'],
|
||||||
|
'custom_login' => customize('get', 'custom_login'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$js_minifier->add('/web/js/site/index.js');
|
$js_minifier->add('/web/js/site/index.js');
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||||||
$rl = ratelimit('get', 'domain', $domain);
|
$rl = ratelimit('get', 'domain', $domain);
|
||||||
$rlyhosts = relayhost('get');
|
$rlyhosts = relayhost('get');
|
||||||
$domain_footer = mailbox('get', 'domain_wide_footer', $domain);
|
$domain_footer = mailbox('get', 'domain_wide_footer', $domain);
|
||||||
|
$mta_sts = mailbox('get', 'mta_sts', $domain);
|
||||||
|
if (count($mta_sts) == 0) {
|
||||||
|
$mta_sts = false;
|
||||||
|
} elseif (isset($mta_sts['mx'])) {
|
||||||
|
$mta_sts['mx'] = implode(',', $mta_sts['mx']);
|
||||||
|
}
|
||||||
$template = 'edit/domain.twig';
|
$template = 'edit/domain.twig';
|
||||||
$template_data = [
|
$template_data = [
|
||||||
'acl' => $_SESSION['acl'],
|
'acl' => $_SESSION['acl'],
|
||||||
@@ -58,6 +64,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||||||
'dkim' => dkim('details', $domain),
|
'dkim' => dkim('details', $domain),
|
||||||
'domain_details' => $result,
|
'domain_details' => $result,
|
||||||
'domain_footer' => $domain_footer,
|
'domain_footer' => $domain_footer,
|
||||||
|
'mta_sts' => $mta_sts,
|
||||||
'mailboxes' => mailbox('get', 'mailboxes', $_GET["domain"]),
|
'mailboxes' => mailbox('get', 'mailboxes', $_GET["domain"]),
|
||||||
'aliases' => mailbox('get', 'aliases', $_GET["domain"], 'address'),
|
'aliases' => mailbox('get', 'aliases', $_GET["domain"], 'address'),
|
||||||
'alias_domains' => mailbox('get', 'alias_domains', $_GET["domain"])
|
'alias_domains' => mailbox('get', 'alias_domains', $_GET["domain"])
|
||||||
@@ -125,6 +132,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
|||||||
'mailbox' => $mailbox,
|
'mailbox' => $mailbox,
|
||||||
'rl' => $rl,
|
'rl' => $rl,
|
||||||
'pushover_data' => $pushover_data,
|
'pushover_data' => $pushover_data,
|
||||||
|
'get_tagging_options' => mailbox('get', 'delimiter_action', $mailbox),
|
||||||
'quarantine_notification' => $quarantine_notification,
|
'quarantine_notification' => $quarantine_notification,
|
||||||
'quarantine_category' => $quarantine_category,
|
'quarantine_category' => $quarantine_category,
|
||||||
'get_tls_policy' => $get_tls_policy,
|
'get_tls_policy' => $get_tls_policy,
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 17 KiB |
@@ -71,6 +71,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||||||
// Init records array
|
// Init records array
|
||||||
$spf_link = '<a href="http://www.open-spf.org/SPF_Record_Syntax/" target="_blank">SPF Record Syntax</a><br />';
|
$spf_link = '<a href="http://www.open-spf.org/SPF_Record_Syntax/" target="_blank">SPF Record Syntax</a><br />';
|
||||||
$dmarc_link = '<a href="https://www.kitterman.com/dmarc/assistant.html" target="_blank">DMARC Assistant</a>';
|
$dmarc_link = '<a href="https://www.kitterman.com/dmarc/assistant.html" target="_blank">DMARC Assistant</a>';
|
||||||
|
$mtasts_report_link = '<a href="https://mxtoolbox.com/dmarc/smtp-tls/how-to-setup-smtp-tls-reports" target="_blank">TLS Report Record Syntax</a>';
|
||||||
|
|
||||||
$records = array();
|
$records = array();
|
||||||
|
|
||||||
@@ -128,6 +129,27 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$mta_sts = mailbox('get', 'mta_sts', $domain);
|
||||||
|
if (count($mta_sts) > 0 && $mta_sts['active'] == 1) {
|
||||||
|
if (!in_array($domain, $alias_domains)) {
|
||||||
|
$records[] = array(
|
||||||
|
'mta-sts.' . $domain,
|
||||||
|
'CNAME',
|
||||||
|
$mailcow_hostname
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$records[] = array(
|
||||||
|
'_mta-sts.' . $domain,
|
||||||
|
'TXT',
|
||||||
|
"v={$mta_sts['version']};id={$mta_sts['id']};",
|
||||||
|
);
|
||||||
|
$records[] = array(
|
||||||
|
'_smtp._tls.' . $domain,
|
||||||
|
'TXT',
|
||||||
|
$mtasts_report_link,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$records[] = array(
|
$records[] = array(
|
||||||
$domain,
|
$domain,
|
||||||
'TXT',
|
'TXT',
|
||||||
@@ -341,15 +363,25 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($currents as &$current) {
|
foreach ($currents as &$current) {
|
||||||
|
if ($current['type'] == "TXT" &&
|
||||||
|
stripos(strtolower($current['txt']), 'v=sts') === 0) {
|
||||||
|
if (strtolower($current[$data_field[$current['type']]]) == strtolower($record[2])) {
|
||||||
|
$state = state_good;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$state = state_nomatch;
|
||||||
|
}
|
||||||
|
$state .= '<br />' . $current[$data_field[$current['type']]];
|
||||||
|
}
|
||||||
if ($current['type'] == 'TXT' &&
|
if ($current['type'] == 'TXT' &&
|
||||||
stripos($current['txt'], 'v=dmarc') === 0 &&
|
stripos($current['txt'], 'v=dmarc') === 0 &&
|
||||||
$record[2] == $dmarc_link) {
|
$record[2] == $dmarc_link) {
|
||||||
$current['txt'] = str_replace(' ', '', $current['txt']);
|
$current['txt'] = str_replace(' ', '', $current['txt']);
|
||||||
$state = $current[$data_field[$current['type']]] . state_optional;
|
$state = $current[$data_field[$current['type']]] . state_optional;
|
||||||
}
|
}
|
||||||
elseif ($current['type'] == 'TXT' &&
|
elseif ($current['type'] == 'TXT' &&
|
||||||
stripos($current['txt'], 'v=spf') === 0 &&
|
stripos($current['txt'], 'v=spf') === 0 &&
|
||||||
$record[2] == $spf_link) {
|
$record[2] == $spf_link) {
|
||||||
$state = state_nomatch;
|
$state = state_nomatch;
|
||||||
$rslt = get_spf_allowed_hosts($record[0], true);
|
$rslt = get_spf_allowed_hosts($record[0], true);
|
||||||
if (in_array($ip, $rslt) && in_array(expand_ipv6($ip6), $rslt)) {
|
if (in_array($ip, $rslt) && in_array(expand_ipv6($ip6), $rslt)) {
|
||||||
@@ -358,8 +390,8 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||||||
$state .= '<br />' . $current[$data_field[$current['type']]] . state_optional;
|
$state .= '<br />' . $current[$data_field[$current['type']]] . state_optional;
|
||||||
}
|
}
|
||||||
elseif ($current['type'] == 'TXT' &&
|
elseif ($current['type'] == 'TXT' &&
|
||||||
stripos($current['txt'], 'v=dkim') === 0 &&
|
stripos($current['txt'], 'v=dkim') === 0 &&
|
||||||
stripos($record[2], 'v=dkim') === 0) {
|
stripos($record[2], 'v=dkim') === 0) {
|
||||||
preg_match('/v=DKIM1;.*k=rsa;.*p=([^;]*).*/i', $current[$data_field[$current['type']]], $dkim_matches_current);
|
preg_match('/v=DKIM1;.*k=rsa;.*p=([^;]*).*/i', $current[$data_field[$current['type']]], $dkim_matches_current);
|
||||||
preg_match('/v=DKIM1;.*k=rsa;.*p=([^;]*).*/i', $record[2], $dkim_matches_good);
|
preg_match('/v=DKIM1;.*k=rsa;.*p=([^;]*).*/i', $record[2], $dkim_matches_good);
|
||||||
if ($dkim_matches_current[1] == $dkim_matches_good[1]) {
|
if ($dkim_matches_current[1] == $dkim_matches_good[1]) {
|
||||||
@@ -367,7 +399,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif ($current['type'] != 'TXT' &&
|
elseif ($current['type'] != 'TXT' &&
|
||||||
isset($data_field[$current['type']]) && $state != state_good) {
|
isset($data_field[$current['type']]) && $state != state_good) {
|
||||||
$state = state_nomatch;
|
$state = state_nomatch;
|
||||||
if ($current[$data_field[$current['type']]] == $record[2]) {
|
if ($current[$data_field[$current['type']]] == $record[2]) {
|
||||||
$state = state_good;
|
$state = state_good;
|
||||||
|
|||||||
@@ -26,23 +26,25 @@ if (is_array($alertbox_log_parser)) {
|
|||||||
|
|
||||||
// map tfa details for twig
|
// map tfa details for twig
|
||||||
$pending_tfa_authmechs = [];
|
$pending_tfa_authmechs = [];
|
||||||
foreach($_SESSION['pending_tfa_methods'] as $authdata){
|
if (array_key_exists('pending_tfa_methods', $_SESSION)) {
|
||||||
$pending_tfa_authmechs[$authdata['authmech']] = false;
|
foreach($_SESSION['pending_tfa_methods'] as $authdata){
|
||||||
}
|
$pending_tfa_authmechs[$authdata['authmech']] = false;
|
||||||
if (isset($pending_tfa_authmechs['webauthn'])) {
|
}
|
||||||
$pending_tfa_authmechs['webauthn'] = true;
|
if (isset($pending_tfa_authmechs['webauthn'])) {
|
||||||
}
|
$pending_tfa_authmechs['webauthn'] = true;
|
||||||
if (!isset($pending_tfa_authmechs['webauthn'])
|
}
|
||||||
&& isset($pending_tfa_authmechs['yubi_otp'])) {
|
if (!isset($pending_tfa_authmechs['webauthn'])
|
||||||
$pending_tfa_authmechs['yubi_otp'] = true;
|
&& isset($pending_tfa_authmechs['yubi_otp'])) {
|
||||||
}
|
$pending_tfa_authmechs['yubi_otp'] = true;
|
||||||
if (!isset($pending_tfa_authmechs['webauthn'])
|
}
|
||||||
&& !isset($pending_tfa_authmechs['yubi_otp'])
|
if (!isset($pending_tfa_authmechs['webauthn'])
|
||||||
&& isset($pending_tfa_authmechs['totp'])) {
|
&& !isset($pending_tfa_authmechs['yubi_otp'])
|
||||||
$pending_tfa_authmechs['totp'] = true;
|
&& isset($pending_tfa_authmechs['totp'])) {
|
||||||
}
|
$pending_tfa_authmechs['totp'] = true;
|
||||||
if (isset($pending_tfa_authmechs['u2f'])) {
|
}
|
||||||
$pending_tfa_authmechs['u2f'] = true;
|
if (isset($pending_tfa_authmechs['u2f'])) {
|
||||||
|
$pending_tfa_authmechs['u2f'] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// globals
|
// globals
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
function app_passwd($_action, $_data = null) {
|
function app_passwd($_action, $_data = null) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
global $lang;
|
global $lang;
|
||||||
$_data_log = $_data;
|
$_data_log = $_data;
|
||||||
!isset($_data_log['app_passwd']) ?: $_data_log['app_passwd'] = '*';
|
!isset($_data_log['app_passwd']) ?: $_data_log['app_passwd'] = '*';
|
||||||
!isset($_data_log['app_passwd2']) ?: $_data_log['app_passwd2'] = '*';
|
!isset($_data_log['app_passwd2']) ?: $_data_log['app_passwd2'] = '*';
|
||||||
@@ -43,20 +43,7 @@ function app_passwd($_action, $_data = null) {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
|
if (password_check($password, $password2) !== true) {
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
|
||||||
'msg' => 'password_complexity'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ($password != $password2) {
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
|
||||||
'msg' => 'password_mismatch'
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$password_hashed = hash_password($password);
|
$password_hashed = hash_password($password);
|
||||||
@@ -88,15 +75,15 @@ function app_passwd($_action, $_data = null) {
|
|||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => 'app_passwd_added'
|
'msg' => 'app_passwd_added'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'edit':
|
case 'edit':
|
||||||
$ids = (array)$_data['id'];
|
$ids = (array)$_data['id'];
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
$is_now = app_passwd('details', $id);
|
$is_now = app_passwd('details', $id);
|
||||||
if (!empty($is_now)) {
|
if (!empty($is_now)) {
|
||||||
$app_name = (!empty($_data['app_name'])) ? $_data['app_name'] : $is_now['name'];
|
$app_name = (!empty($_data['app_name'])) ? $_data['app_name'] : $is_now['name'];
|
||||||
$password = (!empty($_data['password'])) ? $_data['password'] : null;
|
$password = (!empty($_data['app_passwd'])) ? $_data['app_passwd'] : null;
|
||||||
$password2 = (!empty($_data['password2'])) ? $_data['password2'] : null;
|
$password2 = (!empty($_data['app_passwd2'])) ? $_data['app_passwd2'] : null;
|
||||||
if (isset($_data['protocols'])) {
|
if (isset($_data['protocols'])) {
|
||||||
$protocols = (array)$_data['protocols'];
|
$protocols = (array)$_data['protocols'];
|
||||||
$imap_access = (in_array('imap_access', $protocols)) ? 1 : 0;
|
$imap_access = (in_array('imap_access', $protocols)) ? 1 : 0;
|
||||||
@@ -126,20 +113,7 @@ function app_passwd($_action, $_data = null) {
|
|||||||
}
|
}
|
||||||
$app_name = htmlspecialchars(trim($app_name));
|
$app_name = htmlspecialchars(trim($app_name));
|
||||||
if (!empty($password) && !empty($password2)) {
|
if (!empty($password) && !empty($password2)) {
|
||||||
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
|
if (password_check($password, $password2) !== true) {
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
|
||||||
'msg' => 'password_complexity'
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($password != $password2) {
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
|
||||||
'msg' => 'password_mismatch'
|
|
||||||
);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$password_hashed = hash_password($password);
|
$password_hashed = hash_password($password);
|
||||||
@@ -182,7 +156,7 @@ function app_passwd($_action, $_data = null) {
|
|||||||
'msg' => array('object_modified', htmlspecialchars(implode(', ', $ids)))
|
'msg' => array('object_modified', htmlspecialchars(implode(', ', $ids)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$ids = (array)$_data['id'];
|
$ids = (array)$_data['id'];
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
@@ -213,19 +187,17 @@ function app_passwd($_action, $_data = null) {
|
|||||||
'msg' => array('app_passwd_removed', htmlspecialchars($id))
|
'msg' => array('app_passwd_removed', htmlspecialchars($id))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'get':
|
case 'get':
|
||||||
$app_passwds = array();
|
$app_passwds = array();
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `name` FROM `app_passwd` WHERE `mailbox` = :username");
|
$stmt = $pdo->prepare("SELECT `id`, `name` FROM `app_passwd` WHERE `mailbox` = :username");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
$app_passwds = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$app_passwds = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
return $app_passwds;
|
return $app_passwds;
|
||||||
break;
|
break;
|
||||||
case 'details':
|
case 'details':
|
||||||
$app_passwd_data = array();
|
$app_passwd_data = array();
|
||||||
$stmt = $pdo->prepare("SELECT *
|
$stmt = $pdo->prepare("SELECT * FROM `app_passwd` WHERE `id` = :id");
|
||||||
FROM `app_passwd`
|
|
||||||
WHERE `id` = :id");
|
|
||||||
$stmt->execute(array(':id' => $_data));
|
$stmt->execute(array(':id' => $_data));
|
||||||
$app_passwd_data = $stmt->fetch(PDO::FETCH_ASSOC);
|
$app_passwd_data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if (empty($app_passwd_data)) {
|
if (empty($app_passwd_data)) {
|
||||||
@@ -237,6 +209,6 @@ function app_passwd($_action, $_data = null) {
|
|||||||
}
|
}
|
||||||
$app_passwd_data['name'] = htmlspecialchars(trim($app_passwd_data['name']));
|
$app_passwd_data['name'] = htmlspecialchars(trim($app_passwd_data['name']));
|
||||||
return $app_passwd_data;
|
return $app_passwd_data;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,25 +9,52 @@ function check_login($user, $pass, $app_passwd_data = false, $extra = null) {
|
|||||||
// Try validate admin
|
// Try validate admin
|
||||||
if (!isset($role) || $role == "admin") {
|
if (!isset($role) || $role == "admin") {
|
||||||
$result = admin_login($user, $pass);
|
$result = admin_login($user, $pass);
|
||||||
if ($result !== false) return $result;
|
if ($result !== false){
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try validate domain admin
|
// Try validate domain admin
|
||||||
if (!isset($role) || $role == "domain_admin") {
|
if (!isset($role) || $role == "domain_admin") {
|
||||||
$result = domainadmin_login($user, $pass);
|
$result = domainadmin_login($user, $pass);
|
||||||
if ($result !== false) return $result;
|
if ($result !== false) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Try validate app password
|
||||||
|
if (!isset($role) || $role == "app") {
|
||||||
|
$result = apppass_login($user, $pass, $app_passwd_data);
|
||||||
|
if ($result !== false) {
|
||||||
|
if ($app_passwd_data['eas'] === true) {
|
||||||
|
$service = 'EAS';
|
||||||
|
} elseif ($app_passwd_data['dav'] === true) {
|
||||||
|
$service = 'DAV';
|
||||||
|
} else {
|
||||||
|
$service = 'NONE';
|
||||||
|
}
|
||||||
|
$real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
|
||||||
|
set_sasl_log($user, $real_rip, $service, $pass);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try validate user
|
// Try validate user
|
||||||
if (!isset($role) || $role == "user") {
|
if (!isset($role) || $role == "user") {
|
||||||
$result = user_login($user, $pass);
|
$result = user_login($user, $pass);
|
||||||
if ($result !== false) return $result;
|
if ($result !== false) {
|
||||||
}
|
if ($app_passwd_data['eas'] === true) {
|
||||||
|
$service = 'EAS';
|
||||||
// Try validate app password
|
} elseif ($app_passwd_data['dav'] === true) {
|
||||||
if (!isset($role) || $role == "app") {
|
$service = 'DAV';
|
||||||
$result = apppass_login($user, $pass, $app_passwd_data);
|
} else {
|
||||||
if ($result !== false) return $result;
|
$service = 'MAILCOWUI';
|
||||||
|
}
|
||||||
|
$real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
|
||||||
|
set_sasl_log($user, $real_rip, $service);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip log and only return false if it's an internal request
|
// skip log and only return false if it's an internal request
|
||||||
@@ -166,6 +193,7 @@ function user_login($user, $pass, $extra = null){
|
|||||||
global $iam_settings;
|
global $iam_settings;
|
||||||
|
|
||||||
$is_internal = $extra['is_internal'];
|
$is_internal = $extra['is_internal'];
|
||||||
|
$service = $extra['service'];
|
||||||
|
|
||||||
if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
|
if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
|
||||||
if (!$is_internal){
|
if (!$is_internal){
|
||||||
@@ -208,6 +236,14 @@ function user_login($user, $pass, $extra = null){
|
|||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if (!empty($row)) {
|
if (!empty($row)) {
|
||||||
|
// check if user has access to service (imap, smtp, pop3, sieve) if service is set
|
||||||
|
$row['attributes'] = json_decode($row['attributes'], true);
|
||||||
|
if (isset($service)) {
|
||||||
|
$key = strtolower($service) . "_access";
|
||||||
|
if (isset($row['attributes'][$key]) && $row['attributes'][$key] != '1') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,6 +251,14 @@ function user_login($user, $pass, $extra = null){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if user has access to service (imap, smtp, pop3, sieve) if service is set
|
||||||
|
$row['attributes'] = json_decode($row['attributes'], true);
|
||||||
|
if (isset($service)) {
|
||||||
|
$key = strtolower($service) . "_access";
|
||||||
|
if (isset($row['attributes'][$key]) && $row['attributes'][$key] != '1') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
switch ($row['authsource']) {
|
switch ($row['authsource']) {
|
||||||
case 'keycloak':
|
case 'keycloak':
|
||||||
// user authsource is keycloak, try using via rest flow
|
// user authsource is keycloak, try using via rest flow
|
||||||
@@ -324,6 +368,11 @@ function user_login($user, $pass, $extra = null){
|
|||||||
}
|
}
|
||||||
// verify password
|
// verify password
|
||||||
if (verify_hash($row['password'], $pass) !== false) {
|
if (verify_hash($row['password'], $pass) !== false) {
|
||||||
|
|
||||||
|
if (intval($row['attributes']['force_pw_update']) == 1) {
|
||||||
|
$_SESSION['pending_pw_update'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
// check for tfa authenticators
|
// check for tfa authenticators
|
||||||
$authenticators = get_tfa($user);
|
$authenticators = get_tfa($user);
|
||||||
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
|
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
|
||||||
@@ -415,21 +464,7 @@ function apppass_login($user, $pass, $app_passwd_data, $extra = null){
|
|||||||
|
|
||||||
// verify password
|
// verify password
|
||||||
if (verify_hash($row['password'], $pass) !== false) {
|
if (verify_hash($row['password'], $pass) !== false) {
|
||||||
if ($is_internal){
|
$_SESSION['app_passwd_id'] = $row['app_passwd_id'];
|
||||||
$remote_addr = $extra['remote_addr'];
|
|
||||||
} else {
|
|
||||||
$remote_addr = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$service = strtoupper($is_app_passwd);
|
|
||||||
$stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
|
|
||||||
$stmt->execute(array(
|
|
||||||
':service' => $service,
|
|
||||||
':app_id' => $row['app_passwd_id'],
|
|
||||||
':username' => $user,
|
|
||||||
':remote_addr' => $remote_addr
|
|
||||||
));
|
|
||||||
|
|
||||||
unset($_SESSION['ldelay']);
|
unset($_SESSION['ldelay']);
|
||||||
return "user";
|
return "user";
|
||||||
}
|
}
|
||||||
@@ -458,6 +493,9 @@ function keycloak_mbox_login_rest($user, $pass, $extra = null){
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!$iam_provider) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// get access_token for service account of mailcow client
|
// get access_token for service account of mailcow client
|
||||||
$admin_token = identity_provider("get-keycloak-admin-token");
|
$admin_token = identity_provider("get-keycloak-admin-token");
|
||||||
@@ -527,6 +565,17 @@ function keycloak_mbox_login_rest($user, $pass, $extra = null){
|
|||||||
return 'user';
|
return 'user';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if login provisioning is enabled before creating user
|
||||||
|
if (!$iam_settings['login_provisioning']){
|
||||||
|
if (!$is_internal){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, "Auto-create users on login is deactivated"),
|
||||||
|
'msg' => 'login_failed'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// check if matching attribute exist
|
// check if matching attribute exist
|
||||||
if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) {
|
if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) {
|
||||||
if (!empty($iam_settings['default_template'])) {
|
if (!empty($iam_settings['default_template'])) {
|
||||||
@@ -640,10 +689,21 @@ function ldap_mbox_login($user, $pass, $extra = null){
|
|||||||
return 'user';
|
return 'user';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if login provisioning is enabled before creating user
|
||||||
|
if (!$iam_settings['login_provisioning']){
|
||||||
|
if (!$is_internal){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, "Auto-create users on login is deactivated"),
|
||||||
|
'msg' => 'login_failed'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// check if matching attribute exist
|
// check if matching attribute exist
|
||||||
if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) {
|
if (empty($iam_settings['mappers']) || !$user_template || $mapper_key === false) {
|
||||||
if (!empty($iam_settings['default_tempalte'])) {
|
if (!empty($iam_settings['default_template'])) {
|
||||||
$mbox_template = $iam_settings['default_tempalte'];
|
$mbox_template = $iam_settings['default_template'];
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
|
|||||||
@@ -204,6 +204,35 @@ function customize($_action, $_item, $_data = null) {
|
|||||||
'msg' => 'ip_check_opt_in_modified'
|
'msg' => 'ip_check_opt_in_modified'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case 'custom_login':
|
||||||
|
$hide_user_quicklink = ($_data['hide_user_quicklink'] == "1") ? 1 : 0;
|
||||||
|
$hide_domainadmin_quicklink = ($_data['hide_domainadmin_quicklink'] == "1") ? 1 : 0;
|
||||||
|
$hide_admin_quicklink = ($_data['hide_admin_quicklink'] == "1") ? 1 : 0;
|
||||||
|
$force_sso = ($_data['force_sso'] == "1") ? 1 : 0;
|
||||||
|
|
||||||
|
$custom_login = array(
|
||||||
|
"hide_user_quicklink" => $hide_user_quicklink,
|
||||||
|
"hide_domainadmin_quicklink" => $hide_domainadmin_quicklink,
|
||||||
|
"hide_admin_quicklink" => $hide_admin_quicklink,
|
||||||
|
"force_sso" => $force_sso,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
$redis->set('CUSTOM_LOGIN', json_encode($custom_login));
|
||||||
|
}
|
||||||
|
catch (RedisException $e) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||||
|
'msg' => array('redis_error', $e)
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||||
|
'msg' => 'custom_login_modified'
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
@@ -264,7 +293,7 @@ function customize($_action, $_item, $_data = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($app_links)){
|
if (empty($app_links)){
|
||||||
return false;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert from old style
|
// convert from old style
|
||||||
@@ -296,8 +325,10 @@ function customize($_action, $_item, $_data = null) {
|
|||||||
break;
|
break;
|
||||||
case 'ui_texts':
|
case 'ui_texts':
|
||||||
try {
|
try {
|
||||||
$data['title_name'] = ($title_name = $redis->get('TITLE_NAME')) ? $title_name : 'mailcow UI';
|
$mailcow_hostname = strtolower(getenv("MAILCOW_HOSTNAME"));
|
||||||
$data['main_name'] = ($main_name = $redis->get('MAIN_NAME')) ? $main_name : 'mailcow UI';
|
|
||||||
|
$data['title_name'] = ($title_name = $redis->get('TITLE_NAME')) ? $title_name : "$mailcow_hostname - mail UI";
|
||||||
|
$data['main_name'] = ($main_name = $redis->get('MAIN_NAME')) ? $main_name : "$mailcow_hostname - mail UI";
|
||||||
$data['apps_name'] = ($apps_name = $redis->get('APPS_NAME')) ? $apps_name : $lang['header']['apps'];
|
$data['apps_name'] = ($apps_name = $redis->get('APPS_NAME')) ? $apps_name : $lang['header']['apps'];
|
||||||
$data['help_text'] = ($help_text = $redis->get('HELP_TEXT')) ? $help_text : false;
|
$data['help_text'] = ($help_text = $redis->get('HELP_TEXT')) ? $help_text : false;
|
||||||
if (!empty($redis->get('UI_IMPRESS'))) {
|
if (!empty($redis->get('UI_IMPRESS'))) {
|
||||||
@@ -357,6 +388,20 @@ function customize($_action, $_item, $_data = null) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'custom_login':
|
||||||
|
try {
|
||||||
|
$custom_login = $redis->get('CUSTOM_LOGIN');
|
||||||
|
return $custom_login ? json_decode($custom_login, true) : array();
|
||||||
|
}
|
||||||
|
catch (RedisException $e) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_item, $_data),
|
||||||
|
'msg' => array('redis_error', $e)
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -350,6 +350,34 @@ function last_login($action, $username, $sasl_limit_days = 7, $ui_offset = 1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
function set_sasl_log($username, $real_rip, $service){
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!empty($_SESSION['app_passwd_id'])) {
|
||||||
|
$app_password = $_SESSION['app_passwd_id'];
|
||||||
|
} else {
|
||||||
|
$app_password = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare('REPLACE INTO `sasl_log` (`username`, `real_rip`, `service`, `app_password`) VALUES (:username, :real_rip, :service, :app_password)');
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':real_rip' => $real_rip,
|
||||||
|
':service' => $service,
|
||||||
|
':app_password' => $app_password
|
||||||
|
));
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_data_log),
|
||||||
|
'msg' => array('mysql_error', $e)
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
function flush_memcached() {
|
function flush_memcached() {
|
||||||
try {
|
try {
|
||||||
$m = new Memcached();
|
$m = new Memcached();
|
||||||
@@ -973,11 +1001,12 @@ function edit_user_account($_data) {
|
|||||||
':password_hashed' => $password_hashed,
|
':password_hashed' => $password_hashed,
|
||||||
':username' => $username
|
':username' => $username
|
||||||
));
|
));
|
||||||
|
$_SESSION['pending_pw_update'] = false;
|
||||||
|
|
||||||
update_sogo_static_view();
|
update_sogo_static_view();
|
||||||
}
|
}
|
||||||
// edit password recovery email
|
// edit password recovery email
|
||||||
elseif (isset($pw_recovery_email)) {
|
elseif (!empty($password_old) && isset($pw_recovery_email)) {
|
||||||
if (!isset($_SESSION['acl']['pw_reset']) || $_SESSION['acl']['pw_reset'] != "1" ) {
|
if (!isset($_SESSION['acl']['pw_reset']) || $_SESSION['acl']['pw_reset'] != "1" ) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
@@ -987,6 +1016,21 @@ function edit_user_account($_data) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
|
||||||
|
WHERE `kind` NOT REGEXP 'location|thing|group'
|
||||||
|
AND `username` = :user AND authsource = 'mailcow'");
|
||||||
|
$stmt->execute(array(':user' => $username));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!verify_hash($row['password'], $password_old)) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_data_log),
|
||||||
|
'msg' => 'access_denied'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$pw_recovery_email = (!filter_var($pw_recovery_email, FILTER_VALIDATE_EMAIL)) ? '' : $pw_recovery_email;
|
$pw_recovery_email = (!filter_var($pw_recovery_email, FILTER_VALIDATE_EMAIL)) ? '' : $pw_recovery_email;
|
||||||
$stmt = $pdo->prepare("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.recovery_email', :recovery_email)
|
$stmt = $pdo->prepare("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.recovery_email', :recovery_email)
|
||||||
WHERE `username` = :username AND authsource = 'mailcow'");
|
WHERE `username` = :username AND authsource = 'mailcow'");
|
||||||
@@ -1078,11 +1122,21 @@ function user_get_alias_details($username) {
|
|||||||
}
|
}
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
function is_valid_domain_name($domain_name) {
|
function is_valid_domain_name($domain_name, $options = array()) {
|
||||||
if (empty($domain_name)) {
|
if (empty($domain_name)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert domain name to ASCII for validation
|
||||||
$domain_name = idn_to_ascii($domain_name, 0, INTL_IDNA_VARIANT_UTS46);
|
$domain_name = idn_to_ascii($domain_name, 0, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
|
||||||
|
if (isset($options['allow_wildcard']) && $options['allow_wildcard'] == true) {
|
||||||
|
// Remove '*.' if wildcard subdomains are allowed
|
||||||
|
if (strpos($domain_name, '*.') === 0) {
|
||||||
|
$domain_name = substr($domain_name, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name)
|
return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name)
|
||||||
&& preg_match("/^.{1,253}$/", $domain_name)
|
&& preg_match("/^.{1,253}$/", $domain_name)
|
||||||
&& preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name));
|
&& preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name));
|
||||||
@@ -2182,7 +2236,7 @@ function cors($action, $data = null) {
|
|||||||
$cors_settings['allowed_origins'] = $allowed_origins[0];
|
$cors_settings['allowed_origins'] = $allowed_origins[0];
|
||||||
if (in_array('*', $allowed_origins)){
|
if (in_array('*', $allowed_origins)){
|
||||||
$cors_settings['allowed_origins'] = '*';
|
$cors_settings['allowed_origins'] = '*';
|
||||||
} else if (in_array($_SERVER['HTTP_ORIGIN'], $allowed_origins)) {
|
} else if (array_key_exists('HTTP_ORIGIN', $_SERVER) && in_array($_SERVER['HTTP_ORIGIN'], $allowed_origins)) {
|
||||||
$cors_settings['allowed_origins'] = $_SERVER['HTTP_ORIGIN'];
|
$cors_settings['allowed_origins'] = $_SERVER['HTTP_ORIGIN'];
|
||||||
}
|
}
|
||||||
// always allow OPTIONS for preflight request
|
// always allow OPTIONS for preflight request
|
||||||
@@ -2258,12 +2312,14 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
|||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
foreach($rows as $row){
|
foreach($rows as $row){
|
||||||
switch ($row["key"]) {
|
switch ($row["key"]) {
|
||||||
|
case "redirect_url_extra":
|
||||||
case "mappers":
|
case "mappers":
|
||||||
case "templates":
|
case "templates":
|
||||||
$settings[$row["key"]] = json_decode($row["value"]);
|
$settings[$row["key"]] = json_decode($row["value"]);
|
||||||
break;
|
break;
|
||||||
case "use_ssl":
|
case "use_ssl":
|
||||||
case "use_tls":
|
case "use_tls":
|
||||||
|
case "login_provisioning":
|
||||||
case "ignore_ssl_errors":
|
case "ignore_ssl_errors":
|
||||||
$settings[$row["key"]] = boolval($row["value"]);
|
$settings[$row["key"]] = boolval($row["value"]);
|
||||||
break;
|
break;
|
||||||
@@ -2272,6 +2328,10 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// set login_provisioning if not exists
|
||||||
|
if (!array_key_exists('login_provisioning', $settings)) {
|
||||||
|
$settings['login_provisioning'] = 1;
|
||||||
|
}
|
||||||
// return default client_scopes for generic-oidc if none is set
|
// return default client_scopes for generic-oidc if none is set
|
||||||
if ($settings["authsource"] == "generic-oidc" && empty($settings["client_scopes"])){
|
if ($settings["authsource"] == "generic-oidc" && empty($settings["client_scopes"])){
|
||||||
$settings["client_scopes"] = "openid profile email mailcow_template";
|
$settings["client_scopes"] = "openid profile email mailcow_template";
|
||||||
@@ -2336,7 +2396,8 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$_data['ignore_ssl_error'] = isset($_data['ignore_ssl_error']) ? boolval($_data['ignore_ssl_error']) : false;
|
$_data['ignore_ssl_error'] = isset($_data['ignore_ssl_error']) ? boolval($_data['ignore_ssl_error']) : false;
|
||||||
|
$_data['login_provisioning'] = isset($_data['login_provisioning']) ? boolval($_data['login_provisioning']) : false;
|
||||||
switch ($_data['authsource']) {
|
switch ($_data['authsource']) {
|
||||||
case "keycloak":
|
case "keycloak":
|
||||||
$_data['server_url'] = (!empty($_data['server_url'])) ? rtrim($_data['server_url'], '/') : null;
|
$_data['server_url'] = (!empty($_data['server_url'])) ? rtrim($_data['server_url'], '/') : null;
|
||||||
@@ -2345,14 +2406,14 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
|||||||
$_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
|
$_data['import_users'] = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
|
||||||
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
|
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
|
||||||
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
|
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
|
||||||
$required_settings = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_interval', 'ignore_ssl_error');
|
$required_settings = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_interval', 'ignore_ssl_error', 'login_provisioning');
|
||||||
break;
|
break;
|
||||||
case "generic-oidc":
|
case "generic-oidc":
|
||||||
$_data['authorize_url'] = (!empty($_data['authorize_url'])) ? $_data['authorize_url'] : null;
|
$_data['authorize_url'] = (!empty($_data['authorize_url'])) ? $_data['authorize_url'] : null;
|
||||||
$_data['token_url'] = (!empty($_data['token_url'])) ? $_data['token_url'] : null;
|
$_data['token_url'] = (!empty($_data['token_url'])) ? $_data['token_url'] : null;
|
||||||
$_data['userinfo_url'] = (!empty($_data['userinfo_url'])) ? $_data['userinfo_url'] : null;
|
$_data['userinfo_url'] = (!empty($_data['userinfo_url'])) ? $_data['userinfo_url'] : null;
|
||||||
$_data['client_scopes'] = (!empty($_data['client_scopes'])) ? $_data['client_scopes'] : "openid profile email mailcow_template";
|
$_data['client_scopes'] = (!empty($_data['client_scopes'])) ? $_data['client_scopes'] : "openid profile email mailcow_template";
|
||||||
$required_settings = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes', 'ignore_ssl_error');
|
$required_settings = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes', 'ignore_ssl_error', 'login_provisioning');
|
||||||
break;
|
break;
|
||||||
case "ldap":
|
case "ldap":
|
||||||
$_data['host'] = (!empty($_data['host'])) ? str_replace(" ", "", $_data['host']) : "";
|
$_data['host'] = (!empty($_data['host'])) ? str_replace(" ", "", $_data['host']) : "";
|
||||||
@@ -2366,7 +2427,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
|||||||
$_data['use_tls'] = isset($_data['use_tls']) && !$_data['use_ssl'] ? boolval($_data['use_tls']) : false;
|
$_data['use_tls'] = isset($_data['use_tls']) && !$_data['use_ssl'] ? boolval($_data['use_tls']) : false;
|
||||||
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
|
$_data['sync_interval'] = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
|
||||||
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
|
$_data['sync_interval'] = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
|
||||||
$required_settings = array('authsource', 'host', 'port', 'basedn', 'username_field', 'filter', 'attribute_field', 'binddn', 'bindpass', 'periodic_sync', 'import_users', 'sync_interval', 'use_ssl', 'use_tls', 'ignore_ssl_error');
|
$required_settings = array('authsource', 'host', 'port', 'basedn', 'username_field', 'filter', 'attribute_field', 'binddn', 'bindpass', 'periodic_sync', 'import_users', 'sync_interval', 'use_ssl', 'use_tls', 'ignore_ssl_error', 'login_provisioning');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2390,6 +2451,18 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
|||||||
}
|
}
|
||||||
$pdo->commit();
|
$pdo->commit();
|
||||||
|
|
||||||
|
// add redirect_url_extra
|
||||||
|
if (isset($_data['redirect_url_extra'])){
|
||||||
|
$_data['redirect_url_extra'] = (!is_array($_data['redirect_url_extra'])) ? array($_data['redirect_url_extra']) : $_data['redirect_url_extra'];
|
||||||
|
|
||||||
|
$redirect_url_extra = array_filter($_data['redirect_url_extra']);
|
||||||
|
$redirect_url_extra = json_encode($redirect_url_extra);
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO identity_provider (`key`, `value`) VALUES ('redirect_url_extra', :value) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);");
|
||||||
|
$stmt->bindParam(':value', $redirect_url_extra);
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
// add default template
|
// add default template
|
||||||
if (isset($_data['default_template'])) {
|
if (isset($_data['default_template'])) {
|
||||||
$_data['default_template'] = (empty($_data['default_template'])) ? "" : $_data['default_template'];
|
$_data['default_template'] = (empty($_data['default_template'])) ? "" : $_data['default_template'];
|
||||||
@@ -2724,6 +2797,16 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// user doesn't exist, check if login provisioning is enabled
|
||||||
|
if (!$iam_settings['login_provisioning']){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, "Auto-create users on login is deactivated"),
|
||||||
|
'msg' => 'login_failed'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($iam_settings['mappers']) || empty($user_template) || $mapper_key === false){
|
if (empty($iam_settings['mappers']) || empty($user_template) || $mapper_key === false){
|
||||||
if (!empty($iam_settings['default_template'])) {
|
if (!empty($iam_settings['default_template'])) {
|
||||||
$mbox_template = $iam_settings['default_template'];
|
$mbox_template = $iam_settings['default_template'];
|
||||||
@@ -2823,7 +2906,19 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
|
|||||||
case "get-redirect":
|
case "get-redirect":
|
||||||
if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc')
|
if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc')
|
||||||
return false;
|
return false;
|
||||||
$authUrl = $iam_provider->getAuthorizationUrl();
|
$options = [];
|
||||||
|
if (isset($iam_settings['redirect_url_extra'])) {
|
||||||
|
// check if the current domain is used in an extra redirect URL
|
||||||
|
$targetDomain = strtolower($_SERVER['HTTP_HOST']);
|
||||||
|
foreach ($iam_settings['redirect_url_extra'] as $testUrl) {
|
||||||
|
$testUrlParsed = parse_url($testUrl);
|
||||||
|
if (isset($testUrlParsed['host']) && strtolower($testUrlParsed['host']) == $targetDomain) {
|
||||||
|
$options['redirect_uri'] = $testUrl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$authUrl = $iam_provider->getAuthorizationUrl($options);
|
||||||
$_SESSION['oauth2state'] = $iam_provider->getState();
|
$_SESSION['oauth2state'] = $iam_provider->getState();
|
||||||
return $authUrl;
|
return $authUrl;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
// Default to 1 yr
|
// Default to 1 yr
|
||||||
$_data["validity"] = 8760;
|
$_data["validity"] = 8760;
|
||||||
}
|
}
|
||||||
|
if (isset($_data["permanent"]) && filter_var($_data["permanent"], FILTER_VALIDATE_BOOL)) {
|
||||||
|
$permanent = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$permanent = 0;
|
||||||
|
}
|
||||||
$domain = $_data['domain'];
|
$domain = $_data['domain'];
|
||||||
$description = $_data['description'];
|
$description = $_data['description'];
|
||||||
$valid_domains[] = mailbox('get', 'mailbox_details', $username)['domain'];
|
$valid_domains[] = mailbox('get', 'mailbox_details', $username)['domain'];
|
||||||
@@ -65,13 +71,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$validity = strtotime("+" . $_data["validity"] . " hour");
|
$validity = strtotime("+" . $_data["validity"] . " hour");
|
||||||
$stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `description`, `goto`, `validity`) VALUES
|
$stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `description`, `goto`, `validity`, `permanent`) VALUES
|
||||||
(:address, :description, :goto, :validity)");
|
(:address, :description, :goto, :validity, :permanent)");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':address' => readable_random_string(rand(rand(3, 9), rand(3, 9))) . '.' . readable_random_string(rand(rand(3, 9), rand(3, 9))) . '@' . $domain,
|
':address' => readable_random_string(rand(rand(3, 9), rand(3, 9))) . '.' . readable_random_string(rand(rand(3, 9), rand(3, 9))) . '@' . $domain,
|
||||||
':description' => $description,
|
':description' => $description,
|
||||||
':goto' => $username,
|
':goto' => $username,
|
||||||
':validity' => $validity
|
':validity' => $validity,
|
||||||
|
':permanent' => $permanent
|
||||||
));
|
));
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
@@ -684,15 +691,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
return true;
|
return true;
|
||||||
break;
|
break;
|
||||||
case 'alias':
|
case 'alias':
|
||||||
$addresses = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['address']));
|
$addresses = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['address']));
|
||||||
$gotos = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['goto']));
|
$gotos = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['goto']));
|
||||||
$active = intval($_data['active']);
|
$internal = intval($_data['internal']);
|
||||||
$sogo_visible = intval($_data['sogo_visible']);
|
$active = intval($_data['active']);
|
||||||
$goto_null = intval($_data['goto_null']);
|
$sogo_visible = intval($_data['sogo_visible']);
|
||||||
$goto_spam = intval($_data['goto_spam']);
|
$goto_null = intval($_data['goto_null']);
|
||||||
$goto_ham = intval($_data['goto_ham']);
|
$goto_spam = intval($_data['goto_spam']);
|
||||||
|
$goto_ham = intval($_data['goto_ham']);
|
||||||
$private_comment = $_data['private_comment'];
|
$private_comment = $_data['private_comment'];
|
||||||
$public_comment = $_data['public_comment'];
|
$public_comment = $_data['public_comment'];
|
||||||
if (strlen($private_comment) > 160 | strlen($public_comment) > 160){
|
if (strlen($private_comment) > 160 | strlen($public_comment) > 160){
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
@@ -842,8 +850,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `public_comment`, `private_comment`, `goto`, `domain`, `sogo_visible`, `active`)
|
$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `public_comment`, `private_comment`, `goto`, `domain`, `sogo_visible`, `internal`, `active`)
|
||||||
VALUES (:address, :public_comment, :private_comment, :goto, :domain, :sogo_visible, :active)");
|
VALUES (:address, :public_comment, :private_comment, :goto, :domain, :sogo_visible, :internal, :active)");
|
||||||
if (!filter_var($address, FILTER_VALIDATE_EMAIL) === true) {
|
if (!filter_var($address, FILTER_VALIDATE_EMAIL) === true) {
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':address' => '@'.$domain,
|
':address' => '@'.$domain,
|
||||||
@@ -853,6 +861,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
':goto' => $goto,
|
':goto' => $goto,
|
||||||
':domain' => $domain,
|
':domain' => $domain,
|
||||||
':sogo_visible' => $sogo_visible,
|
':sogo_visible' => $sogo_visible,
|
||||||
|
':internal' => $internal,
|
||||||
':active' => $active
|
':active' => $active
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -864,6 +873,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
':goto' => $goto,
|
':goto' => $goto,
|
||||||
':domain' => $domain,
|
':domain' => $domain,
|
||||||
':sogo_visible' => $sogo_visible,
|
':sogo_visible' => $sogo_visible,
|
||||||
|
':internal' => $internal,
|
||||||
':active' => $active
|
':active' => $active
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -1223,6 +1233,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username
|
':username' => $username
|
||||||
));
|
));
|
||||||
|
// save delimiter_action
|
||||||
|
if (isset($_data['tagged_mail_handler'])) {
|
||||||
|
mailbox('edit', 'delimiter_action', array(
|
||||||
|
'username' => $username,
|
||||||
|
'tagged_mail_handler' => $_data['tagged_mail_handler']
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// save tags
|
// save tags
|
||||||
foreach($tags as $index => $tag){
|
foreach($tags as $index => $tag){
|
||||||
if (empty($tag)) continue;
|
if (empty($tag)) continue;
|
||||||
@@ -1392,6 +1410,80 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
|
|
||||||
return mailbox('add', 'mailbox', $mailbox_attributes);
|
return mailbox('add', 'mailbox', $mailbox_attributes);
|
||||||
break;
|
break;
|
||||||
|
case 'mta_sts':
|
||||||
|
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
$version = strtolower($_data['version']);
|
||||||
|
$mode = strtolower($_data['mode']);
|
||||||
|
$mx = explode(",", preg_replace('/\s+/', '', $_data['mx']));
|
||||||
|
$max_age = intval($_data['max_age']);
|
||||||
|
$active = (intval($_data['active']) == 1) ? 1 : 0;
|
||||||
|
$id = date('YmdHis');
|
||||||
|
|
||||||
|
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => 'access_denied'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (empty($version) || !in_array($version, array('stsv1'))) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => array('version_invalid', htmlspecialchars($domain))
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (empty($mode) || !in_array($mode, array('enforce', 'testing', 'none'))) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => array('mode_invalid', htmlspecialchars($domain))
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (empty($max_age) || $max_age < 0 || $max_age > 31536000) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => array('max_age_invalid', htmlspecialchars($domain))
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach ($mx as $index => $mx_domain) {
|
||||||
|
$mx_domain = idn_to_ascii(strtolower(trim($mx_domain)), 0, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
if (!is_valid_domain_name($mx_domain, array('allow_wildcard' => true))) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => array('mx_invalid', htmlspecialchars($mx_domain))
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO `mta_sts` (`id`, `domain`, `version`, `mode`, `mx`, `max_age`, `active`)
|
||||||
|
VALUES (:id, :domain, :version, :mode, :mx, :max_age, :active)");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':id' => $id,
|
||||||
|
':domain' => $domain,
|
||||||
|
':version' => $version,
|
||||||
|
':mode' => $mode,
|
||||||
|
':mx' => implode(",", $mx),
|
||||||
|
':max_age' => $max_age,
|
||||||
|
':active' => $active
|
||||||
|
));
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data),
|
||||||
|
'msg' => $e->getMessage()
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
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);
|
||||||
$description = $_data['description'];
|
$description = $_data['description'];
|
||||||
@@ -1613,6 +1705,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$attr = array();
|
$attr = array();
|
||||||
$attr["quota"] = isset($_data['quota']) ? intval($_data['quota']) * 1048576 : 0;
|
$attr["quota"] = isset($_data['quota']) ? intval($_data['quota']) * 1048576 : 0;
|
||||||
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : array();
|
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : array();
|
||||||
|
$attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['tagged_mail_handler']);
|
||||||
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
|
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
|
||||||
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
|
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
|
||||||
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s";
|
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s";
|
||||||
@@ -2017,15 +2110,23 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (empty($_data['validity'])) {
|
if (empty($_data['validity']) && empty($_data['permanent'])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$validity = round((int)time() + ($_data['validity'] * 3600));
|
if (isset($_data['permanent']) && filter_var($_data['permanent'], FILTER_VALIDATE_BOOL)) {
|
||||||
$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = :validity WHERE
|
$permanent = 1;
|
||||||
|
$validity = 0;
|
||||||
|
}
|
||||||
|
else if (isset($_data['validity'])) {
|
||||||
|
$permanent = 0;
|
||||||
|
$validity = round((int)time() + ($_data['validity'] * 3600));
|
||||||
|
}
|
||||||
|
$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = :validity, `permanent` = :permanent WHERE
|
||||||
`address` = :address");
|
`address` = :address");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':address' => $address,
|
':address' => $address,
|
||||||
':validity' => $validity
|
':validity' => $validity,
|
||||||
|
':permanent' => $permanent
|
||||||
));
|
));
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
@@ -2398,6 +2499,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
$is_now = mailbox('get', 'alias_details', $id);
|
$is_now = mailbox('get', 'alias_details', $id);
|
||||||
if (!empty($is_now)) {
|
if (!empty($is_now)) {
|
||||||
|
$internal = (isset($_data['internal'])) ? intval($_data['internal']) : $is_now['internal'];
|
||||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
|
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
|
||||||
$sogo_visible = (isset($_data['sogo_visible'])) ? intval($_data['sogo_visible']) : $is_now['sogo_visible'];
|
$sogo_visible = (isset($_data['sogo_visible'])) ? intval($_data['sogo_visible']) : $is_now['sogo_visible'];
|
||||||
$goto_null = (isset($_data['goto_null'])) ? intval($_data['goto_null']) : 0;
|
$goto_null = (isset($_data['goto_null'])) ? intval($_data['goto_null']) : 0;
|
||||||
@@ -2583,6 +2685,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
`domain` = :domain,
|
`domain` = :domain,
|
||||||
`goto` = :goto,
|
`goto` = :goto,
|
||||||
`sogo_visible`= :sogo_visible,
|
`sogo_visible`= :sogo_visible,
|
||||||
|
`internal`= :internal,
|
||||||
`active`= :active
|
`active`= :active
|
||||||
WHERE `id` = :id");
|
WHERE `id` = :id");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
@@ -2592,6 +2695,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
':domain' => $domain,
|
':domain' => $domain,
|
||||||
':goto' => $goto,
|
':goto' => $goto,
|
||||||
':sogo_visible' => $sogo_visible,
|
':sogo_visible' => $sogo_visible,
|
||||||
|
':internal' => $internal,
|
||||||
':active' => $active,
|
':active' => $active,
|
||||||
':id' => $is_now['id']
|
':id' => $is_now['id']
|
||||||
));
|
));
|
||||||
@@ -3259,6 +3363,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// save delimiter_action
|
||||||
|
if (isset($_data['tagged_mail_handler'])) {
|
||||||
|
mailbox('edit', 'delimiter_action', array(
|
||||||
|
'username' => $username,
|
||||||
|
'tagged_mail_handler' => $_data['tagged_mail_handler']
|
||||||
|
));
|
||||||
|
}
|
||||||
// save tags
|
// save tags
|
||||||
foreach($tags as $index => $tag){
|
foreach($tags as $index => $tag){
|
||||||
if (empty($tag)) continue;
|
if (empty($tag)) continue;
|
||||||
@@ -3604,6 +3715,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$attr = array();
|
$attr = array();
|
||||||
$attr["quota"] = isset($_data['quota']) ? intval($_data['quota']) * 1048576 : 0;
|
$attr["quota"] = isset($_data['quota']) ? intval($_data['quota']) * 1048576 : 0;
|
||||||
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : $is_now['tags'];
|
$attr['tags'] = (isset($_data['tags'])) ? $_data['tags'] : $is_now['tags'];
|
||||||
|
$attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : $is_now['tagged_mail_handler'];
|
||||||
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification'];
|
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification'];
|
||||||
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category'];
|
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category'];
|
||||||
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : $is_now['rl_frame'];
|
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : $is_now['rl_frame'];
|
||||||
@@ -3724,6 +3836,125 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
break;
|
break;
|
||||||
|
case 'mta_sts':
|
||||||
|
if (!is_array($_data['domains'])) {
|
||||||
|
$domains = array();
|
||||||
|
$domains[] = $_data['domains'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$domains = $_data['domains'];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($domains as $domain) {
|
||||||
|
$domain = idn_to_ascii(strtolower(trim($domain)), 0, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
|
||||||
|
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => 'access_denied'
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_now = mailbox('get', 'mta_sts', $domain);
|
||||||
|
if (!empty($is_now)) {
|
||||||
|
$version = (isset($_data['version'])) ? strtolower($_data['version']) : $is_now['version'];
|
||||||
|
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
|
||||||
|
$active = ($active == 1) ? 1 : 0;
|
||||||
|
$mode = (isset($_data['mode'])) ? strtolower($_data['mode']) : $is_now['mode'];
|
||||||
|
$mx = (isset($_data['mx'])) ? explode(",", preg_replace('/\s+/', '', $_data['mx'])) : $is_now['mx'];
|
||||||
|
$max_age = (isset($_data['max_age'])) ? intval($_data['max_age']) : $is_now['max_age'];
|
||||||
|
|
||||||
|
// Update ID if neccesary
|
||||||
|
if ($version != strtolower($is_now['version']) ||
|
||||||
|
$mode != strtolower($is_now['mode']) ||
|
||||||
|
$mx != $is_now['mx'] ||
|
||||||
|
$max_age != $is_now['max_age']) {
|
||||||
|
$id = date('YmdHis');
|
||||||
|
} else {
|
||||||
|
$id = $is_now['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||||
|
'msg' => 'access_denied'
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($version) || !in_array($version, array('stsv1'))) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => array('version_invalid', htmlspecialchars($version))
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (empty($mode) || !in_array($mode, array('enforce', 'testing', 'none'))) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => array('mode_invalid', htmlspecialchars($domain))
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (empty($max_age) || $max_age < 0 || $max_age > 31557600) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => array('max_age_invalid', htmlspecialchars($domain))
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($mx as $index => $mx_domain) {
|
||||||
|
$mx_domain = idn_to_ascii(strtolower(trim($mx_domain)), 0, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
$invalid_mx = false;
|
||||||
|
if (!is_valid_domain_name($mx_domain, array('allow_wildcard' => true))) {
|
||||||
|
$invalid_mx = $mx_domain;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($invalid_mx) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => array('mx_invalid', htmlspecialchars($invalid_mx))
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("UPDATE `mta_sts` SET `id` = :id, `version` = :version, `mode` = :mode, `mx` = :mx, `max_age` = :max_age, `active` = :active WHERE `domain` = :domain");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':id' => $id,
|
||||||
|
':domain' => $domain,
|
||||||
|
':version' => $version,
|
||||||
|
':mode' => $mode,
|
||||||
|
':mx' => implode(",", $mx),
|
||||||
|
':max_age' => $max_age,
|
||||||
|
':active' => $active
|
||||||
|
));
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data),
|
||||||
|
'msg' => $e->getMessage()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_type, $_data, $_attr),
|
||||||
|
'msg' => array('object_modified', $domain)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
case 'resource':
|
case 'resource':
|
||||||
if (!is_array($_data['name'])) {
|
if (!is_array($_data['name'])) {
|
||||||
$names = array();
|
$names = array();
|
||||||
@@ -4368,10 +4599,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
`description`,
|
`description`,
|
||||||
`validity`,
|
`validity`,
|
||||||
`created`,
|
`created`,
|
||||||
`modified`
|
`modified`,
|
||||||
|
`permanent`
|
||||||
FROM `spamalias`
|
FROM `spamalias`
|
||||||
WHERE `goto` = :username
|
WHERE `goto` = :username
|
||||||
AND `validity` >= :unixnow");
|
AND (`validity` >= :unixnow
|
||||||
|
OR `permanent` != 0)");
|
||||||
$stmt->execute(array(':username' => $_data, ':unixnow' => time()));
|
$stmt->execute(array(':username' => $_data, ':unixnow' => time()));
|
||||||
$tladata = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$tladata = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
return $tladata;
|
return $tladata;
|
||||||
@@ -4490,6 +4723,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
`address`,
|
`address`,
|
||||||
`public_comment`,
|
`public_comment`,
|
||||||
`private_comment`,
|
`private_comment`,
|
||||||
|
`internal`,
|
||||||
`active`,
|
`active`,
|
||||||
`sogo_visible`,
|
`sogo_visible`,
|
||||||
`created`,
|
`created`,
|
||||||
@@ -4520,6 +4754,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$aliasdata['goto'] = $row['goto'];
|
$aliasdata['goto'] = $row['goto'];
|
||||||
$aliasdata['address'] = $row['address'];
|
$aliasdata['address'] = $row['address'];
|
||||||
(!filter_var($aliasdata['address'], FILTER_VALIDATE_EMAIL)) ? $aliasdata['is_catch_all'] = 1 : $aliasdata['is_catch_all'] = 0;
|
(!filter_var($aliasdata['address'], FILTER_VALIDATE_EMAIL)) ? $aliasdata['is_catch_all'] = 1 : $aliasdata['is_catch_all'] = 0;
|
||||||
|
$aliasdata['internal'] = $row['internal'];
|
||||||
$aliasdata['active'] = $row['active'];
|
$aliasdata['active'] = $row['active'];
|
||||||
$aliasdata['active_int'] = $row['active'];
|
$aliasdata['active_int'] = $row['active'];
|
||||||
$aliasdata['sogo_visible'] = $row['sogo_visible'];
|
$aliasdata['sogo_visible'] = $row['sogo_visible'];
|
||||||
@@ -4944,7 +5179,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE (`kind` = '' OR `kind` = NULL) AND `domain` = :domain AND `username` != :username");
|
$stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE (`kind` = '' OR `kind` = NULL) AND `domain` = :domain AND `username` != :username");
|
||||||
$stmt->execute(array(':domain' => $row['domain'], ':username' => $_data));
|
$stmt->execute(array(':domain' => $row['domain'], ':username' => $_data));
|
||||||
$MailboxUsage = $stmt->fetch(PDO::FETCH_ASSOC);
|
$MailboxUsage = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
$stmt = $pdo->prepare("SELECT IFNULL(COUNT(`address`), 0) AS `sa_count` FROM `spamalias` WHERE `goto` = :address AND `validity` >= :unixnow");
|
$stmt = $pdo->prepare("SELECT IFNULL(COUNT(`address`), 0) AS `sa_count` FROM `spamalias` WHERE `goto` = :address AND (`validity` >= :unixnow OR `permanent` != 0)");
|
||||||
$stmt->execute(array(':address' => $_data, ':unixnow' => time()));
|
$stmt->execute(array(':address' => $_data, ':unixnow' => time()));
|
||||||
$SpamaliasUsage = $stmt->fetch(PDO::FETCH_ASSOC);
|
$SpamaliasUsage = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
$mailboxdata['max_new_quota'] = ($DomainQuota['quota'] * 1048576) - $MailboxUsage['in_use'];
|
$mailboxdata['max_new_quota'] = ($DomainQuota['quota'] * 1048576) - $MailboxUsage['in_use'];
|
||||||
@@ -5012,6 +5247,20 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
return $rows;
|
return $rows;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'mta_sts':
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM `mta_sts` WHERE `domain` = :domain");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':domain' => $_data,
|
||||||
|
));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if (empty($row)){
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$row['mx'] = explode(',', $row['mx']);
|
||||||
|
$row['version'] = strtoupper(substr($row['version'], 0, 3)) . substr($row['version'], 3);
|
||||||
|
|
||||||
|
return $row;
|
||||||
|
break;
|
||||||
case 'resource_details':
|
case 'resource_details':
|
||||||
$resourcedata = array();
|
$resourcedata = array();
|
||||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
|
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
|
||||||
@@ -5397,6 +5646,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':domain' => $domain,
|
':domain' => $domain,
|
||||||
));
|
));
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `mta_sts` WHERE `domain` = :domain");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':domain' => $domain,
|
||||||
|
));
|
||||||
$stmt = $pdo->query("DELETE FROM `admin` WHERE `superadmin` = 0 AND `username` NOT IN (SELECT `username`FROM `domain_admins`);");
|
$stmt = $pdo->query("DELETE FROM `admin` WHERE `superadmin` = 0 AND `username` NOT IN (SELECT `username`FROM `domain_admins`);");
|
||||||
$stmt = $pdo->query("DELETE FROM `da_acl` WHERE `username` NOT IN (SELECT `username`FROM `domain_admins`);");
|
$stmt = $pdo->query("DELETE FROM `da_acl` WHERE `username` NOT IN (SELECT `username`FROM `domain_admins`);");
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function quarantine($_action, $_data = null) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
||||||
WHERE SHA2(CONCAT(`id`, `qid`), 256) = :hash
|
WHERE `qhash` = :hash
|
||||||
AND user_acl.quarantine = 1
|
AND user_acl.quarantine = 1
|
||||||
AND rcpt IN (SELECT username FROM mailbox)');
|
AND rcpt IN (SELECT username FROM mailbox)');
|
||||||
$stmt->execute(array(':hash' => $hash));
|
$stmt->execute(array(':hash' => $hash));
|
||||||
@@ -65,7 +65,7 @@ function quarantine($_action, $_data = null) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
||||||
WHERE SHA2(CONCAT(`id`, `qid`), 256) = :hash
|
WHERE `qhash` = :hash
|
||||||
AND `user_acl`.`quarantine` = 1
|
AND `user_acl`.`quarantine` = 1
|
||||||
AND `username` IN (SELECT `username` FROM `mailbox`)');
|
AND `username` IN (SELECT `username` FROM `mailbox`)');
|
||||||
$stmt->execute(array(':hash' => $hash));
|
$stmt->execute(array(':hash' => $hash));
|
||||||
@@ -169,7 +169,7 @@ function quarantine($_action, $_data = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif ($release_format == 'raw') {
|
elseif ($release_format == 'raw') {
|
||||||
$detail_row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/', 'X-Pre-Release-Spam-Flag $1', $detail_row['msg']);
|
$detail_row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $detail_row['msg']);
|
||||||
$postfix_talk = array(
|
$postfix_talk = array(
|
||||||
array('220', 'HELO quarantine' . chr(10)),
|
array('220', 'HELO quarantine' . chr(10)),
|
||||||
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
||||||
@@ -464,7 +464,7 @@ function quarantine($_action, $_data = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif ($release_format == 'raw') {
|
elseif ($release_format == 'raw') {
|
||||||
$row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/', 'X-Pre-Release-Spam-Flag $1', $row['msg']);
|
$row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $row['msg']);
|
||||||
$postfix_talk = array(
|
$postfix_talk = array(
|
||||||
array('220', 'HELO quarantine' . chr(10)),
|
array('220', 'HELO quarantine' . chr(10)),
|
||||||
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
||||||
@@ -833,7 +833,7 @@ function quarantine($_action, $_data = null) {
|
|||||||
)));
|
)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE SHA2(CONCAT(`id`, `qid`), 256) = :hash');
|
$stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE `qhash` = :hash');
|
||||||
$stmt->execute(array(':hash' => $hash));
|
$stmt->execute(array(':hash' => $hash));
|
||||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -62,7 +62,11 @@ if ($app_links_processed){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround to get text with <br> straight to twig.
|
||||||
|
// Using "nl2br" doesn't work with Twig as it would escape everything by default.
|
||||||
|
if (isset($UI_TEXTS["ui_footer"])) {
|
||||||
|
$UI_TEXTS["ui_footer"] = nl2br($UI_TEXTS["ui_footer"]);
|
||||||
|
}
|
||||||
|
|
||||||
$globalVariables = [
|
$globalVariables = [
|
||||||
'mailcow_hostname' => getenv('MAILCOW_HOSTNAME'),
|
'mailcow_hostname' => getenv('MAILCOW_HOSTNAME'),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ function init_db_schema()
|
|||||||
try {
|
try {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|
||||||
$db_version = "27012025_1555";
|
$db_version = "10312025_0525";
|
||||||
|
|
||||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
@@ -184,6 +184,7 @@ function init_db_schema()
|
|||||||
"private_comment" => "TEXT",
|
"private_comment" => "TEXT",
|
||||||
"public_comment" => "TEXT",
|
"public_comment" => "TEXT",
|
||||||
"sogo_visible" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
"sogo_visible" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||||
|
"internal" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||||
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
||||||
),
|
),
|
||||||
"keys" => array(
|
"keys" => array(
|
||||||
@@ -345,10 +346,14 @@ function init_db_schema()
|
|||||||
"notified" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
"notified" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||||
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||||
"user" => "VARCHAR(255) NOT NULL DEFAULT 'unknown'",
|
"user" => "VARCHAR(255) NOT NULL DEFAULT 'unknown'",
|
||||||
|
"qhash" => "VARCHAR(64)",
|
||||||
),
|
),
|
||||||
"keys" => array(
|
"keys" => array(
|
||||||
"primary" => array(
|
"primary" => array(
|
||||||
"" => array("id")
|
"" => array("id")
|
||||||
|
),
|
||||||
|
"key" => array(
|
||||||
|
"qhash" => array("qhash")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
@@ -471,6 +476,23 @@ function init_db_schema()
|
|||||||
),
|
),
|
||||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
),
|
),
|
||||||
|
"mta_sts" => array(
|
||||||
|
"cols" => array(
|
||||||
|
"id" => "BIGINT NOT NULL",
|
||||||
|
"domain" => "VARCHAR(255) NOT NULL",
|
||||||
|
"version" => "VARCHAR(255) NOT NULL",
|
||||||
|
"mode" => "VARCHAR(255) NOT NULL",
|
||||||
|
"mx" => "VARCHAR(255) NOT NULL",
|
||||||
|
"max_age" => "VARCHAR(255) NOT NULL",
|
||||||
|
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
|
||||||
|
),
|
||||||
|
"keys" => array(
|
||||||
|
"primary" => array(
|
||||||
|
"" => array("domain")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||||
|
),
|
||||||
"user_acl" => array(
|
"user_acl" => array(
|
||||||
"cols" => array(
|
"cols" => array(
|
||||||
"username" => "VARCHAR(255) NOT NULL",
|
"username" => "VARCHAR(255) NOT NULL",
|
||||||
@@ -532,7 +554,8 @@ function init_db_schema()
|
|||||||
"description" => "TEXT NOT NULL",
|
"description" => "TEXT NOT NULL",
|
||||||
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||||
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
|
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
|
||||||
"validity" => "INT(11)"
|
"validity" => "INT(11)",
|
||||||
|
"permanent" => "TINYINT(1) NOT NULL DEFAULT '0'"
|
||||||
),
|
),
|
||||||
"keys" => array(
|
"keys" => array(
|
||||||
"primary" => array(
|
"primary" => array(
|
||||||
@@ -1315,6 +1338,14 @@ function init_db_schema()
|
|||||||
$pdo->query($create);
|
$pdo->query($create);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear old app_passwd log entries
|
||||||
|
$pdo->exec("DELETE FROM logs
|
||||||
|
WHERE role != 'unauthenticated'
|
||||||
|
AND JSON_EXTRACT(`call`, '$[0]') = 'app_passwd'
|
||||||
|
AND JSON_EXTRACT(`call`, '$[1]') = 'edit'
|
||||||
|
AND (JSON_CONTAINS_PATH(`call`, 'one', '$[2].password')
|
||||||
|
OR JSON_CONTAINS_PATH(`call`, 'one', '$[2].password2'));");
|
||||||
|
|
||||||
// Mitigate imapsync argument injection issue
|
// Mitigate imapsync argument injection issue
|
||||||
$pdo->query("UPDATE `imapsync` SET `custom_params` = ''
|
$pdo->query("UPDATE `imapsync` SET `custom_params` = ''
|
||||||
WHERE `custom_params` LIKE '%pipemess%'
|
WHERE `custom_params` LIKE '%pipemess%'
|
||||||
@@ -1482,6 +1513,10 @@ function init_db_schema()
|
|||||||
'msg' => 'db_init_complete'
|
'msg' => 'db_init_complete'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fill quarantine.qhash
|
||||||
|
$pdo->query("UPDATE `quarantine` SET `qhash` = SHA2(CONCAT(`id`, `qid`), 256) WHERE ISNULL(`qhash`)");
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
if (php_sapi_name() == "cli") {
|
if (php_sapi_name() == "cli") {
|
||||||
echo "DB initialization failed: " . print_r($e, true) . PHP_EOL;
|
echo "DB initialization failed: " . print_r($e, true) . PHP_EOL;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
// Start session
|
// Start session
|
||||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
|
session_name($SESSION_NAME);
|
||||||
ini_set("session.cookie_httponly", 1);
|
ini_set("session.cookie_httponly", 1);
|
||||||
|
ini_set("session.cookie_samesite", $SESSION_SAMESITE_POLICY);
|
||||||
ini_set('session.gc_maxlifetime', $SESSION_LIFETIME);
|
ini_set('session.gc_maxlifetime', $SESSION_LIFETIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,8 +76,11 @@ if (isset($_POST["verify_tfa_login"])) {
|
|||||||
|
|
||||||
$user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']);
|
$user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']);
|
||||||
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
|
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
|
||||||
if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) {
|
if (intval($user_details['attributes']['sogo_access']) == 1 &&
|
||||||
header("Location: /SOGo/so/{$_SESSION['mailcow_cc_username']}");
|
intval($user_details['attributes']['force_pw_update']) != 1 &&
|
||||||
|
getenv('SKIP_SOGO') != "y" &&
|
||||||
|
!$is_dual) {
|
||||||
|
header("Location: /SOGo/so/");
|
||||||
die();
|
die();
|
||||||
} else {
|
} else {
|
||||||
header("Location: /user");
|
header("Location: /user");
|
||||||
@@ -139,8 +142,11 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
|||||||
|
|
||||||
$user_details = mailbox("get", "mailbox_details", $login_user);
|
$user_details = mailbox("get", "mailbox_details", $login_user);
|
||||||
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
|
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
|
||||||
if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) {
|
if (intval($user_details['attributes']['sogo_access']) == 1 &&
|
||||||
header("Location: /SOGo/so/{$login_user}");
|
intval($user_details['attributes']['force_pw_update']) != 1 &&
|
||||||
|
getenv('SKIP_SOGO') != "y" &&
|
||||||
|
!$is_dual) {
|
||||||
|
header("Location: /SOGo/so/");
|
||||||
die();
|
die();
|
||||||
} else {
|
} else {
|
||||||
header("Location: /user");
|
header("Location: /user");
|
||||||
|
|||||||
@@ -83,8 +83,9 @@ $DEFAULT_LANG = 'en-gb';
|
|||||||
// https://en.wikipedia.org/wiki/IETF_language_tag
|
// https://en.wikipedia.org/wiki/IETF_language_tag
|
||||||
$AVAILABLE_LANGUAGES = array(
|
$AVAILABLE_LANGUAGES = array(
|
||||||
// 'ca-es' => 'Català (Catalan)',
|
// 'ca-es' => 'Català (Catalan)',
|
||||||
|
'bg-bg' => 'Български (Bulgarian)',
|
||||||
'cs-cz' => 'Čeština (Czech)',
|
'cs-cz' => 'Čeština (Czech)',
|
||||||
'da-dk' => 'Danish (Dansk)',
|
'da-dk' => 'Dansk (Danish)',
|
||||||
'de-de' => 'Deutsch (German)',
|
'de-de' => 'Deutsch (German)',
|
||||||
'en-gb' => 'English',
|
'en-gb' => 'English',
|
||||||
'es-es' => 'Español (Spanish)',
|
'es-es' => 'Español (Spanish)',
|
||||||
@@ -109,6 +110,7 @@ $AVAILABLE_LANGUAGES = array(
|
|||||||
'sv-se' => 'Svenska (Swedish)',
|
'sv-se' => 'Svenska (Swedish)',
|
||||||
'tr-tr' => 'Türkçe (Turkish)',
|
'tr-tr' => 'Türkçe (Turkish)',
|
||||||
'uk-ua' => 'Українська (Ukrainian)',
|
'uk-ua' => 'Українська (Ukrainian)',
|
||||||
|
'vi-vn' => 'Tiếng Việt (Vietnamese)',
|
||||||
'zh-cn' => '简体中文 (Simplified Chinese)',
|
'zh-cn' => '简体中文 (Simplified Chinese)',
|
||||||
'zh-tw' => '繁體中文 (Traditional Chinese)',
|
'zh-tw' => '繁體中文 (Traditional Chinese)',
|
||||||
);
|
);
|
||||||
@@ -152,6 +154,13 @@ $LOG_PAGINATION_SIZE = 50;
|
|||||||
// Session lifetime in seconds
|
// Session lifetime in seconds
|
||||||
$SESSION_LIFETIME = 10800;
|
$SESSION_LIFETIME = 10800;
|
||||||
|
|
||||||
|
// Session SameSite Policy
|
||||||
|
// Use "None", "Lax" or "Strict"
|
||||||
|
$SESSION_SAMESITE_POLICY = "Lax";
|
||||||
|
|
||||||
|
// Name of the session cookie
|
||||||
|
$SESSION_NAME = "MCSESSID";
|
||||||
|
|
||||||
// Label for OTP devices
|
// Label for OTP devices
|
||||||
$OTP_LABEL = "mailcow UI";
|
$OTP_LABEL = "mailcow UI";
|
||||||
|
|
||||||
@@ -185,6 +194,12 @@ $MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update'] = false;
|
|||||||
// Enable SOGo access - Users will be redirected to SOGo after login (set to false to disable redirect by default)
|
// Enable SOGo access - Users will be redirected to SOGo after login (set to false to disable redirect by default)
|
||||||
$MAILBOX_DEFAULT_ATTRIBUTES['sogo_access'] = true;
|
$MAILBOX_DEFAULT_ATTRIBUTES['sogo_access'] = true;
|
||||||
|
|
||||||
|
// How to handle tagged emails
|
||||||
|
// none - No special handling
|
||||||
|
// subfolder - Create subfolder under INBOX (e.g. "INBOX/Facebook")
|
||||||
|
// subject - Add tag to subject (e.g. "[Facebook] Subject")
|
||||||
|
$MAILBOX_DEFAULT_ATTRIBUTES['tagged_mail_handler'] = "none";
|
||||||
|
|
||||||
// Send notification when quarantine is not empty (never, hourly, daily, weekly)
|
// Send notification when quarantine is not empty (never, hourly, daily, weekly)
|
||||||
$MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification'] = 'hourly';
|
$MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification'] = 'hourly';
|
||||||
|
|
||||||
@@ -237,12 +252,12 @@ $FIDO2_FORMATS = array('apple', 'android-key', 'android-safetynet', 'fido-u2f',
|
|||||||
// Set visible Rspamd maps in mailcow UI, do not change unless you know what you are doing
|
// Set visible Rspamd maps in mailcow UI, do not change unless you know what you are doing
|
||||||
$RSPAMD_MAPS = array(
|
$RSPAMD_MAPS = array(
|
||||||
'regex' => array(
|
'regex' => array(
|
||||||
'Header-From: Blacklist' => 'global_mime_from_blacklist.map',
|
'Header-From: Denylist' => 'global_mime_from_blacklist.map',
|
||||||
'Header-From: Whitelist' => 'global_mime_from_whitelist.map',
|
'Header-From: Allowlist' => 'global_mime_from_whitelist.map',
|
||||||
'Envelope Sender Blacklist' => 'global_smtp_from_blacklist.map',
|
'Envelope Sender Denylist' => 'global_smtp_from_blacklist.map',
|
||||||
'Envelope Sender Whitelist' => 'global_smtp_from_whitelist.map',
|
'Envelope Sender Allowlist' => 'global_smtp_from_whitelist.map',
|
||||||
'Recipient Blacklist' => 'global_rcpt_blacklist.map',
|
'Recipient Denylist' => 'global_rcpt_blacklist.map',
|
||||||
'Recipient Whitelist' => 'global_rcpt_whitelist.map',
|
'Recipient Allowlist' => 'global_rcpt_whitelist.map',
|
||||||
'Fishy TLDS (only fired in combination with bad words)' => 'fishy_tlds.map',
|
'Fishy TLDS (only fired in combination with bad words)' => 'fishy_tlds.map',
|
||||||
'Bad Words (only fired in combination with fishy TLDs)' => 'bad_words.map',
|
'Bad Words (only fired in combination with fishy TLDs)' => 'bad_words.map',
|
||||||
'Bad Words DE (only fired in combination with fishy TLDs)' => 'bad_words_de.map',
|
'Bad Words DE (only fired in combination with fishy TLDs)' => 'bad_words_de.map',
|
||||||
@@ -256,57 +271,57 @@ $RSPAMD_MAPS = array(
|
|||||||
|
|
||||||
$IMAPSYNC_OPTIONS = array(
|
$IMAPSYNC_OPTIONS = array(
|
||||||
'whitelist' => array(
|
'whitelist' => array(
|
||||||
'abort',
|
'abort',
|
||||||
'authmd51',
|
'authmd51',
|
||||||
'authmd52',
|
'authmd52',
|
||||||
'authmech1',
|
'authmech1',
|
||||||
'authmech2',
|
'authmech2',
|
||||||
'authuser1',
|
'authuser1',
|
||||||
'authuser2',
|
'authuser2',
|
||||||
'debug',
|
'debug',
|
||||||
'debugcontent',
|
'debugcontent',
|
||||||
'debugcrossduplicates',
|
'debugcrossduplicates',
|
||||||
'debugflags',
|
'debugflags',
|
||||||
'debugfolders',
|
'debugfolders',
|
||||||
'debugimap',
|
'debugimap',
|
||||||
'debugimap1',
|
'debugimap1',
|
||||||
'debugimap2',
|
'debugimap2',
|
||||||
'debugmemory',
|
'debugmemory',
|
||||||
'debugssl',
|
'debugssl',
|
||||||
'delete1emptyfolders',
|
'delete1emptyfolders',
|
||||||
'delete2folders',
|
'delete2folders',
|
||||||
'disarmreadreceipts',
|
'disarmreadreceipts',
|
||||||
'domain1',
|
'domain1',
|
||||||
'domain2',
|
'domain2',
|
||||||
'domino1',
|
'domino1',
|
||||||
'domino2',
|
'domino2',
|
||||||
'dry',
|
'dry',
|
||||||
'errorsmax',
|
'errorsmax',
|
||||||
'exchange1',
|
'exchange1',
|
||||||
'exchange2',
|
'exchange2',
|
||||||
'exitwhenover',
|
'exitwhenover',
|
||||||
'expunge1',
|
'expunge1',
|
||||||
'f1f2',
|
'f1f2',
|
||||||
'filterbuggyflags',
|
'filterbuggyflags',
|
||||||
'folder',
|
'folder',
|
||||||
'folderfirst',
|
'folderfirst',
|
||||||
'folderlast',
|
'folderlast',
|
||||||
'folderrec',
|
'folderrec',
|
||||||
'gmail1',
|
'gmail1',
|
||||||
'gmail2',
|
'gmail2',
|
||||||
'idatefromheader',
|
'idatefromheader',
|
||||||
'include',
|
'include',
|
||||||
'inet4',
|
'inet4',
|
||||||
'inet6',
|
'inet6',
|
||||||
'justconnect',
|
'justconnect',
|
||||||
'justfolders',
|
'justfolders',
|
||||||
'justfoldersizes',
|
'justfoldersizes',
|
||||||
'justlogin',
|
'justlogin',
|
||||||
'keepalive1',
|
'keepalive1',
|
||||||
'keepalive2',
|
'keepalive2',
|
||||||
'log',
|
'log',
|
||||||
'logdir',
|
'logdir',
|
||||||
'logfile',
|
'logfile',
|
||||||
'maxbytesafter',
|
'maxbytesafter',
|
||||||
'maxlinelength',
|
'maxlinelength',
|
||||||
'maxmessagespersecond',
|
'maxmessagespersecond',
|
||||||
@@ -314,62 +329,62 @@ $IMAPSYNC_OPTIONS = array(
|
|||||||
'maxsleep',
|
'maxsleep',
|
||||||
'minage',
|
'minage',
|
||||||
'minsize',
|
'minsize',
|
||||||
'noabletosearch',
|
'noabletosearch',
|
||||||
'noabletosearch1',
|
'noabletosearch1',
|
||||||
'noabletosearch2',
|
'noabletosearch2',
|
||||||
'noexpunge1',
|
'noexpunge1',
|
||||||
'noexpunge2',
|
'noexpunge2',
|
||||||
'nofoldersizesatend',
|
'nofoldersizesatend',
|
||||||
'noid',
|
'noid',
|
||||||
'nolog',
|
'nolog',
|
||||||
'nomixfolders',
|
'nomixfolders',
|
||||||
'noresyncflags',
|
'noresyncflags',
|
||||||
'nossl1',
|
'nossl1',
|
||||||
'nossl2',
|
'nossl2',
|
||||||
'nosyncacls',
|
'nosyncacls',
|
||||||
'notls1',
|
'notls1',
|
||||||
'notls2',
|
'notls2',
|
||||||
'nouidexpunge2',
|
'nouidexpunge2',
|
||||||
'nousecache',
|
'nousecache',
|
||||||
'oauthaccesstoken1',
|
'oauthaccesstoken1',
|
||||||
'oauthaccesstoken2',
|
'oauthaccesstoken2',
|
||||||
'oauthdirect1',
|
'oauthdirect1',
|
||||||
'oauthdirect2',
|
'oauthdirect2',
|
||||||
'office1',
|
'office1',
|
||||||
'office2',
|
'office2',
|
||||||
'pidfile',
|
'pidfile',
|
||||||
'pidfilelocking',
|
'pidfilelocking',
|
||||||
'prefix1',
|
'prefix1',
|
||||||
'prefix2',
|
'prefix2',
|
||||||
'proxyauth1',
|
'proxyauth1',
|
||||||
'proxyauth2',
|
'proxyauth2',
|
||||||
'resyncflags',
|
'resyncflags',
|
||||||
'resynclabels',
|
'resynclabels',
|
||||||
'search',
|
'search',
|
||||||
'search1',
|
'search1',
|
||||||
'search2',
|
'search2',
|
||||||
'sep1',
|
'sep1',
|
||||||
'sep2',
|
'sep2',
|
||||||
'showpasswords',
|
'showpasswords',
|
||||||
'skipemptyfolders',
|
'skipemptyfolders',
|
||||||
'ssl2',
|
'ssl2',
|
||||||
'sslargs1',
|
'sslargs1',
|
||||||
'sslargs2',
|
'sslargs2',
|
||||||
'subfolder1',
|
'subfolder1',
|
||||||
'subscribe',
|
'subscribe',
|
||||||
'subscribed',
|
'subscribed',
|
||||||
'syncacls',
|
'syncacls',
|
||||||
'syncduplicates',
|
'syncduplicates',
|
||||||
'syncinternaldates',
|
'syncinternaldates',
|
||||||
'synclabels',
|
'synclabels',
|
||||||
'tests',
|
'tests',
|
||||||
'testslive',
|
'testslive',
|
||||||
'testslive6',
|
'testslive6',
|
||||||
'tls2',
|
'tls2',
|
||||||
'truncmess',
|
'truncmess',
|
||||||
'usecache',
|
'usecache',
|
||||||
'useheader',
|
'useheader',
|
||||||
'useuid'
|
'useuid'
|
||||||
),
|
),
|
||||||
'blacklist' => array(
|
'blacklist' => array(
|
||||||
'skipmess',
|
'skipmess',
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ if (isset($_SESSION['mailcow_cc_role']) && isset($_SESSION['oauth2_request'])) {
|
|||||||
elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') {
|
elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') {
|
||||||
$user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']);
|
$user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']);
|
||||||
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
|
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
|
||||||
if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) {
|
if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual && getenv('SKIP_SOGO') != "y") {
|
||||||
header("Location: /SOGo/so/{$_SESSION['mailcow_cc_username']}");
|
header("Location: /SOGo/so/");
|
||||||
} else {
|
} else {
|
||||||
header("Location: /user");
|
header("Location: /user");
|
||||||
}
|
}
|
||||||
@@ -33,16 +33,18 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING'];
|
|||||||
|
|
||||||
$has_iam_sso = false;
|
$has_iam_sso = false;
|
||||||
if ($iam_provider){
|
if ($iam_provider){
|
||||||
$has_iam_sso = identity_provider("get-redirect") ? true : false;
|
$iam_redirect_url = identity_provider("get-redirect");
|
||||||
|
$has_iam_sso = $iam_redirect_url ? true : false;
|
||||||
}
|
}
|
||||||
|
$custom_login = customize('get', 'custom_login');
|
||||||
|
|
||||||
$template = 'user_index.twig';
|
$template = 'user_index.twig';
|
||||||
$template_data = [
|
$template_data = [
|
||||||
'oauth2_request' => @$_SESSION['oauth2_request'],
|
'oauth2_request' => @$_SESSION['oauth2_request'],
|
||||||
'is_mobileconfig' => str_contains($_SESSION['index_query_string'], 'mobileconfig'),
|
'is_mobileconfig' => str_contains($_SESSION['index_query_string'], 'mobileconfig'),
|
||||||
'login_delay' => @$_SESSION['ldelay'],
|
'login_delay' => @$_SESSION['ldelay'],
|
||||||
'has_iam_sso' => $has_iam_sso
|
'has_iam_sso' => $has_iam_sso,
|
||||||
|
'custom_login' => $custom_login,
|
||||||
];
|
];
|
||||||
|
|
||||||
$js_minifier->add('/web/js/site/index.js');
|
$js_minifier->add('/web/js/site/index.js');
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ $(document).ready(function() {
|
|||||||
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
|
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(".generate_password").click(async function( event ) {
|
$(".generate_password").click(async function( event ) {
|
||||||
try {
|
try {
|
||||||
var password_policy = await window.fetch("/api/v1/get/passwordpolicy", { method:'GET', cache:'no-cache' });
|
var password_policy = await window.fetch("/api/v1/get/passwordpolicy", { method:'GET', cache:'no-cache' });
|
||||||
var password_policy = await password_policy.json();
|
var password_policy = await password_policy.json();
|
||||||
random_passwd_length = password_policy.length;
|
random_passwd_length = password_policy.length;
|
||||||
@@ -48,7 +48,11 @@ $(document).ready(function() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
$(".rot-enc").html(function(){
|
$(".rot-enc").html(function(){
|
||||||
return str_rot13($(this).html())
|
footer_html = $(this).html();
|
||||||
|
footer_html = footer_html.replace(/</g, '<').replace(/>/g, '>')
|
||||||
|
.replace(/&/g, '&').replace(/&nzc;/g, '&')
|
||||||
|
.replace(/"/g, '"').replace(/'/g, "'");
|
||||||
|
return str_rot13(footer_html)
|
||||||
});
|
});
|
||||||
// https://stackoverflow.com/questions/4399005/implementing-jquerys-shake-effect-with-animate
|
// https://stackoverflow.com/questions/4399005/implementing-jquerys-shake-effect-with-animate
|
||||||
function shake(div,interval,distance,times) {
|
function shake(div,interval,distance,times) {
|
||||||
@@ -125,7 +129,7 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// responsive tabs, scroll to opened tab
|
// responsive tabs, scroll to opened tab
|
||||||
$(document).on("shown.bs.collapse shown.bs.tab", function (e) {
|
$(document).on("shown.bs.collapse shown.bs.tab", function (e) {
|
||||||
var target = $(e.target);
|
var target = $(e.target);
|
||||||
@@ -409,4 +413,4 @@ function copyToClipboard(id) {
|
|||||||
// only works with https connections
|
// only works with https connections
|
||||||
navigator.clipboard.writeText(copyText.value);
|
navigator.clipboard.writeText(copyText.value);
|
||||||
mailcow_alert_box(lang.copy_to_clipboard, "success");
|
mailcow_alert_box(lang.copy_to_clipboard, "success");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ jQuery(function($){
|
|||||||
$('.submit_rspamd_regex').attr({"disabled": true});
|
$('.submit_rspamd_regex').attr({"disabled": true});
|
||||||
});
|
});
|
||||||
$("#show_rspamd_global_filters").click(function() {
|
$("#show_rspamd_global_filters").click(function() {
|
||||||
$.get("inc/ajax/show_rspamd_global_filters.php");
|
$.get("/inc/ajax/show_rspamd_global_filters.php");
|
||||||
$("#confirm_show_rspamd_global_filters").hide();
|
$("#confirm_show_rspamd_global_filters").hide();
|
||||||
$("#rspamd_global_filters").removeClass("d-none");
|
$("#rspamd_global_filters").removeClass("d-none");
|
||||||
});
|
});
|
||||||
@@ -558,7 +558,7 @@ jQuery(function($){
|
|||||||
} else if (table == 'oauth2clientstable') {
|
} else if (table == 'oauth2clientstable') {
|
||||||
$.each(data, function (i, item) {
|
$.each(data, function (i, item) {
|
||||||
item.action = '<div class="btn-group">' +
|
item.action = '<div class="btn-group">' +
|
||||||
'<a href="/edit.php?oauth2client=' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
|
'<a href="/edit/oauth2client/' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
|
||||||
'<a href="#" data-action="delete_selected" data-id="single-oauth2-client" data-api-url="delete/oauth2-client" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
|
'<a href="#" data-action="delete_selected" data-id="single-oauth2-client" data-api-url="delete/oauth2-client" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-lg btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
item.scope = "profile";
|
item.scope = "profile";
|
||||||
@@ -573,7 +573,7 @@ jQuery(function($){
|
|||||||
item.action = '<div class="btn-group">' +
|
item.action = '<div class="btn-group">' +
|
||||||
'<a href="/edit/domainadmin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
|
'<a href="/edit/domainadmin/' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-secondary"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>' +
|
||||||
'<a href="#" data-action="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
|
'<a href="#" data-action="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
|
||||||
'<a href="/index.php?duallogin=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-success"><i class="bi bi-person-fill"></i> Login</a>' +
|
'<a href="/domainadmin/?duallogin=' + encodeURIComponent(item.username) + '" class="btn btn-xs btn-xs-lg btn-xs-third btn-success"><i class="bi bi-person-fill"></i> Login</a>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
});
|
});
|
||||||
} else if (table == 'adminstable') {
|
} else if (table == 'adminstable') {
|
||||||
@@ -655,7 +655,7 @@ jQuery(function($){
|
|||||||
$(this).html('<i class="bi bi-arrow-repeat icon-spin"></i> ');
|
$(this).html('<i class="bi bi-arrow-repeat icon-spin"></i> ');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: 'inc/ajax/relay_check.php',
|
url: '/inc/ajax/relay_check.php',
|
||||||
dataType: 'text',
|
dataType: 'text',
|
||||||
data: $('#test_relayhost_form').serialize(),
|
data: $('#test_relayhost_form').serialize(),
|
||||||
complete: function (data) {
|
complete: function (data) {
|
||||||
@@ -681,7 +681,7 @@ jQuery(function($){
|
|||||||
$(this).html('<div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div> ');
|
$(this).html('<div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div> ');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: 'inc/ajax/transport_check.php',
|
url: '/inc/ajax/transport_check.php',
|
||||||
dataType: 'text',
|
dataType: 'text',
|
||||||
data: $('#test_transport_form').serialize(),
|
data: $('#test_transport_form').serialize(),
|
||||||
complete: function (data) {
|
complete: function (data) {
|
||||||
@@ -715,7 +715,6 @@ jQuery(function($){
|
|||||||
$('.app_hide').off('change');
|
$('.app_hide').off('change');
|
||||||
$('.app_hide').on('change', function (e) {
|
$('.app_hide').on('change', function (e) {
|
||||||
var value = $(this).is(':checked') ? '1' : '0';
|
var value = $(this).is(':checked') ? '1' : '0';
|
||||||
console.log(value)
|
|
||||||
$(this).parent().children(':first-child').val(value);
|
$(this).parent().children(':first-child').val(value);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -789,6 +788,18 @@ jQuery(function($){
|
|||||||
$('.iam_ldap_rolemap_del').click(async function(e){
|
$('.iam_ldap_rolemap_del').click(async function(e){
|
||||||
deleteAttributeMappingRow(this, e);
|
deleteAttributeMappingRow(this, e);
|
||||||
});
|
});
|
||||||
|
$('.iam_redirect_add_keycloak').click(async function(e){
|
||||||
|
addRedirectUrlRow('#iam_keycloak_redirect_list', '.iam_keycloak_redirect_del', e);
|
||||||
|
});
|
||||||
|
$('.iam_redirect_add_generic').click(async function(e){
|
||||||
|
addRedirectUrlRow('#iam_generic_redirect_list', '.iam_generic_redirect_del', e);
|
||||||
|
});
|
||||||
|
$('.iam_keycloak_redirect_del').click(async function(e){
|
||||||
|
deleteRedirectUrlRow(this, e);
|
||||||
|
});
|
||||||
|
$('.iam_generic_redirect_del').click(async function(e){
|
||||||
|
deleteRedirectUrlRow(this, e);
|
||||||
|
});
|
||||||
// selecting identity provider
|
// selecting identity provider
|
||||||
$('#iam_provider').on('change', function(){
|
$('#iam_provider').on('change', function(){
|
||||||
// toggle password fields
|
// toggle password fields
|
||||||
@@ -833,4 +844,22 @@ jQuery(function($){
|
|||||||
if ($(elem).parent().parent().parent().parent().children().length > 1)
|
if ($(elem).parent().parent().parent().parent().children().length > 1)
|
||||||
$(elem).parent().parent().parent().remove();
|
$(elem).parent().parent().parent().remove();
|
||||||
}
|
}
|
||||||
|
function addRedirectUrlRow(list_id, del_class, e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var parent = $(list_id)
|
||||||
|
$(parent).children().last().clone().appendTo(parent);
|
||||||
|
var newChild = $(parent).children().last();
|
||||||
|
$(newChild).find('input').val('');
|
||||||
|
|
||||||
|
$(del_class).off('click');
|
||||||
|
$(del_class).click(async function(e){
|
||||||
|
deleteRedirectUrlRow(this, e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function deleteRedirectUrlRow(elem, e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if ($(elem).parent().parent().parent().parent().children().length > 2)
|
||||||
|
$(elem).parent().parent().parent().remove();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ $(document).ready(function() {
|
|||||||
window.fetch("/api/v1/get/status/host/ip", { method:'GET', cache:'no-cache' }).then(function(response) {
|
window.fetch("/api/v1/get/status/host/ip", { method:'GET', cache:'no-cache' }).then(function(response) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}).then(function(data) {
|
}).then(function(data) {
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
// display host ips
|
// display host ips
|
||||||
if (data.ipv4)
|
if (data.ipv4)
|
||||||
$("#host_ipv4").text(data.ipv4);
|
$("#host_ipv4").text(data.ipv4);
|
||||||
@@ -1007,7 +1005,7 @@ jQuery(function($){
|
|||||||
"data-order": cellData.sortBy,
|
"data-order": cellData.sortBy,
|
||||||
"data-sort": cellData.sortBy
|
"data-sort": cellData.sortBy
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render: function (data) {
|
render: function (data) {
|
||||||
return data.value;
|
return data.value;
|
||||||
}
|
}
|
||||||
@@ -1032,7 +1030,7 @@ jQuery(function($){
|
|||||||
"data-order": cellData.sortBy,
|
"data-order": cellData.sortBy,
|
||||||
"data-sort": cellData.sortBy
|
"data-sort": cellData.sortBy
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render: function (data) {
|
render: function (data) {
|
||||||
return data.value;
|
return data.value;
|
||||||
}
|
}
|
||||||
@@ -1348,8 +1346,6 @@ function update_stats(timeout=5){
|
|||||||
window.fetch("/api/v1/get/status/host", {method:'GET',cache:'no-cache'}).then(function(response) {
|
window.fetch("/api/v1/get/status/host", {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}).then(function(data) {
|
}).then(function(data) {
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
if (data){
|
if (data){
|
||||||
// display table data
|
// display table data
|
||||||
$("#host_date").text(data.system_time);
|
$("#host_date").text(data.system_time);
|
||||||
@@ -1399,8 +1395,6 @@ function update_container_stats(timeout=5){
|
|||||||
var diskIOCtx = Chart.getChart(container + "_DiskIOChart");
|
var diskIOCtx = Chart.getChart(container + "_DiskIOChart");
|
||||||
var netIOCtx = Chart.getChart(container + "_NetIOChart");
|
var netIOCtx = Chart.getChart(container + "_NetIOChart");
|
||||||
|
|
||||||
console.log(container);
|
|
||||||
console.log(data);
|
|
||||||
prev_stats = null;
|
prev_stats = null;
|
||||||
if (data.length >= 2){
|
if (data.length >= 2){
|
||||||
prev_stats = data[data.length -2];
|
prev_stats = data[data.length -2];
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ $(document).ready(function() {
|
|||||||
// load tags
|
// load tags
|
||||||
if ($('#tags').length){
|
if ($('#tags').length){
|
||||||
var tagsEl = $('#tags').parent().find('.tag-values')[0];
|
var tagsEl = $('#tags').parent().find('.tag-values')[0];
|
||||||
console.log($(tagsEl).val())
|
|
||||||
var tags = JSON.parse($(tagsEl).val());
|
var tags = JSON.parse($(tagsEl).val());
|
||||||
$(tagsEl).val("");
|
$(tagsEl).val("");
|
||||||
|
|
||||||
|
|||||||
@@ -269,6 +269,24 @@ $(document).ready(function() {
|
|||||||
function setMailboxTemplateData(template){
|
function setMailboxTemplateData(template){
|
||||||
$("#addInputQuota").val(template.quota / 1048576);
|
$("#addInputQuota").val(template.quota / 1048576);
|
||||||
|
|
||||||
|
if (template.tagged_mail_handler === "subfolder"){
|
||||||
|
$('#tagged_mail_handler_subfolder').prop('checked', true);
|
||||||
|
$('#tagged_mail_handler_subject').prop('checked', false);
|
||||||
|
$('#tagged_mail_handler_none').prop('checked', false);
|
||||||
|
} else if(template.tagged_mail_handler === "subject"){
|
||||||
|
$('#tagged_mail_handler_subfolder').prop('checked', false);
|
||||||
|
$('#tagged_mail_handler_subject').prop('checked', true);
|
||||||
|
$('#tagged_mail_handler_none').prop('checked', false);
|
||||||
|
} else if(template.tagged_mail_handler === "none"){
|
||||||
|
$('#tagged_mail_handler_subfolder').prop('checked', false);
|
||||||
|
$('#tagged_mail_handler_subject').prop('checked', false);
|
||||||
|
$('#tagged_mail_handler_none').prop('checked', true);
|
||||||
|
} else {
|
||||||
|
$('#tagged_mail_handler_subfolder').prop('checked', false);
|
||||||
|
$('#tagged_mail_handler_subject').prop('checked', false);
|
||||||
|
$('#tagged_mail_handler_none').prop('checked', true);
|
||||||
|
}
|
||||||
|
|
||||||
if (template.quarantine_notification === "never"){
|
if (template.quarantine_notification === "never"){
|
||||||
$('#quarantine_notification_never').prop('checked', true);
|
$('#quarantine_notification_never').prop('checked', true);
|
||||||
$('#quarantine_notification_hourly').prop('checked', false);
|
$('#quarantine_notification_hourly').prop('checked', false);
|
||||||
@@ -1931,11 +1949,6 @@ jQuery(function($){
|
|||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
responsivePriority: 5,
|
responsivePriority: 5,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: lang.bcc_destinations,
|
|
||||||
data: 'bcc_dest',
|
|
||||||
defaultContent: ''
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: lang.sogo_visible,
|
title: lang.sogo_visible,
|
||||||
data: 'sogo_visible',
|
data: 'sogo_visible',
|
||||||
@@ -1954,6 +1967,15 @@ jQuery(function($){
|
|||||||
data: 'private_comment',
|
data: 'private_comment',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: lang.internal,
|
||||||
|
data: 'internal',
|
||||||
|
defaultContent: '',
|
||||||
|
responsivePriority: 6,
|
||||||
|
render: function (data, type) {
|
||||||
|
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>';
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: lang.active,
|
title: lang.active,
|
||||||
data: 'active',
|
data: 'active',
|
||||||
|
|||||||
@@ -90,31 +90,24 @@ jQuery(function($){
|
|||||||
console.log('error reading last logins');
|
console.log('error reading last logins');
|
||||||
},
|
},
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
$('.last-ui-login').html('');
|
|
||||||
$('.last-sasl-login').html('');
|
$('.last-sasl-login').html('');
|
||||||
if (data.ui.time) {
|
|
||||||
$('.last-ui-login').html('<i class="bi bi-person-fill"></i> ' + lang.last_ui_login + ': ' + unix_time_format(data.ui.time));
|
|
||||||
} else {
|
|
||||||
$('.last-ui-login').text(lang.no_last_login);
|
|
||||||
}
|
|
||||||
if (data.sasl) {
|
if (data.sasl) {
|
||||||
$('.last-sasl-login').append('<ul class="list-group">');
|
$('.last-sasl-login').append('<ul class="list-group">');
|
||||||
$.each(data.sasl, function (i, item) {
|
$.each(data.sasl, function (i, item) {
|
||||||
var datetime = new Date(item.datetime.replace(/-/g, "/"));
|
var datetime = new Date(item.datetime.replace(/-/g, "/"));
|
||||||
var local_datetime = datetime.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
var local_datetime = datetime.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
||||||
var service = '<div class="badge bg-secondary">' + item.service.toUpperCase() + '</div>';
|
var service = '<div class="badge bg-secondary">' + item.service.toUpperCase() + '</div>';
|
||||||
var app_password = item.app_password ? ' <a href="/edit/app-passwd/' + item.app_password + '"><i class="bi bi-app-indicator"></i> ' + escapeHtml(item.app_password_name || "App") + '</a>' : '';
|
var app_password = item.app_password ? ' <a href="/edit/app-passwd/' + item.app_password + '"><i class="bi bi-key-fill"></i><span class="ms-1">' + escapeHtml(item.app_password_name || "App") + '</span></a>' : '';
|
||||||
var real_rip = item.real_rip.startsWith("Web") ? item.real_rip : '<a href="https://bgp.he.net/ip/' + item.real_rip + '" target="_blank">' + item.real_rip + "</a>";
|
var real_rip = item.real_rip.startsWith("Web") ? item.real_rip : '<a href="https://bgp.tools/prefix/' + item.real_rip + '" target="_blank">' + item.real_rip + "</a>";
|
||||||
var ip_location = item.location ? ' <span class="flag-icon flag-icon-' + item.location.toLowerCase() + '"></span>' : '';
|
var ip_location = item.location ? ' <span class="flag-icon flag-icon-' + item.location.toLowerCase() + '"></span>' : '';
|
||||||
var ip_data = real_rip + ip_location + app_password;
|
var ip_data = real_rip + ip_location + app_password;
|
||||||
|
|
||||||
$(".last-sasl-login").append(`
|
$(".last-sasl-login").append(`
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||||
<div class="ms-2 me-auto d-flex flex-column">
|
<div class="ms-2 me-auto d-flex flex-column">
|
||||||
<div class="fw-bold">` + real_rip + `</div>
|
<div class="fw-bold">` + ip_location + real_rip + `</div>
|
||||||
<small class="fst-italic mt-2">` + service + ` ` + local_datetime + `</small>
|
<small class="fst-italic mt-2">` + service + ` ` + local_datetime + `</small>` + app_password + `
|
||||||
</div>
|
</div>
|
||||||
<span>` + ip_location + `</span>
|
|
||||||
</li>
|
</li>
|
||||||
`);
|
`);
|
||||||
})
|
})
|
||||||
@@ -175,7 +168,6 @@ jQuery(function($){
|
|||||||
type: "GET",
|
type: "GET",
|
||||||
url: "/api/v1/get/time_limited_aliases",
|
url: "/api/v1/get/time_limited_aliases",
|
||||||
dataSrc: function(data){
|
dataSrc: function(data){
|
||||||
console.log(data);
|
|
||||||
$.each(data, function (i, item) {
|
$.each(data, function (i, item) {
|
||||||
if (acl_data.spam_alias === 1) {
|
if (acl_data.spam_alias === 1) {
|
||||||
item.action = '<div class="btn-group">' +
|
item.action = '<div class="btn-group">' +
|
||||||
@@ -183,6 +175,10 @@ jQuery(function($){
|
|||||||
'</div>';
|
'</div>';
|
||||||
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />';
|
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />';
|
||||||
item.address = escapeHtml(item.address);
|
item.address = escapeHtml(item.address);
|
||||||
|
item.validity = {
|
||||||
|
value: item.validity,
|
||||||
|
permanent: item.permanent
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
item.chkbox = '<input type="checkbox" class="form-check-input" disabled />';
|
item.chkbox = '<input type="checkbox" class="form-check-input" disabled />';
|
||||||
@@ -226,9 +222,21 @@ jQuery(function($){
|
|||||||
title: lang.alias_valid_until,
|
title: lang.alias_valid_until,
|
||||||
data: 'validity',
|
data: 'validity',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
createdCell: function(td, cellData) {
|
render: function (data, type) {
|
||||||
createSortableDate(td, cellData)
|
var date = new Date(data.value ? data.value * 1000 : 0);
|
||||||
}
|
switch (type) {
|
||||||
|
case "sort":
|
||||||
|
if (data.permanent) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return date.getTime();
|
||||||
|
default:
|
||||||
|
if (data.permanent) {
|
||||||
|
return lang.forever;
|
||||||
|
}
|
||||||
|
return date.toLocaleDateString(LOCALE, DATETIME_FORMAT);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang.created_on,
|
title: lang.created_on,
|
||||||
@@ -268,7 +276,6 @@ jQuery(function($){
|
|||||||
type: "GET",
|
type: "GET",
|
||||||
url: '/api/v1/get/syncjobs/' + encodeURIComponent(mailcow_cc_username) + '/no_log',
|
url: '/api/v1/get/syncjobs/' + encodeURIComponent(mailcow_cc_username) + '/no_log',
|
||||||
dataSrc: function(data){
|
dataSrc: function(data){
|
||||||
console.log(data);
|
|
||||||
$.each(data, function (i, item) {
|
$.each(data, function (i, item) {
|
||||||
item.user1 = escapeHtml(item.user1);
|
item.user1 = escapeHtml(item.user1);
|
||||||
item.log = '<a href="#syncjobLogModal" data-bs-toggle="modal" data-syncjob-id="' + item.id + '">' + lang.open_logs + '</a>'
|
item.log = '<a href="#syncjobLogModal" data-bs-toggle="modal" data-syncjob-id="' + item.id + '">' + lang.open_logs + '</a>'
|
||||||
@@ -424,7 +431,6 @@ jQuery(function($){
|
|||||||
type: "GET",
|
type: "GET",
|
||||||
url: '/api/v1/get/app-passwd/all',
|
url: '/api/v1/get/app-passwd/all',
|
||||||
dataSrc: function(data){
|
dataSrc: function(data){
|
||||||
console.log(data);
|
|
||||||
$.each(data, function (i, item) {
|
$.each(data, function (i, item) {
|
||||||
item.name = escapeHtml(item.name)
|
item.name = escapeHtml(item.name)
|
||||||
item.protocols = []
|
item.protocols = []
|
||||||
@@ -520,7 +526,6 @@ jQuery(function($){
|
|||||||
type: "GET",
|
type: "GET",
|
||||||
url: '/api/v1/get/policy_wl_mailbox',
|
url: '/api/v1/get/policy_wl_mailbox',
|
||||||
dataSrc: function(data){
|
dataSrc: function(data){
|
||||||
console.log(data);
|
|
||||||
$.each(data, function (i, item) {
|
$.each(data, function (i, item) {
|
||||||
if (validateEmail(item.object)) {
|
if (validateEmail(item.object)) {
|
||||||
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="policy_wl_mailbox" name="multi_select" value="' + item.prefid + '" />';
|
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="policy_wl_mailbox" name="multi_select" value="' + item.prefid + '" />';
|
||||||
@@ -591,7 +596,6 @@ jQuery(function($){
|
|||||||
type: "GET",
|
type: "GET",
|
||||||
url: '/api/v1/get/policy_bl_mailbox',
|
url: '/api/v1/get/policy_bl_mailbox',
|
||||||
dataSrc: function(data){
|
dataSrc: function(data){
|
||||||
console.log(data);
|
|
||||||
$.each(data, function (i, item) {
|
$.each(data, function (i, item) {
|
||||||
if (validateEmail(item.object)) {
|
if (validateEmail(item.object)) {
|
||||||
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="policy_bl_mailbox" name="multi_select" value="' + item.prefid + '" />';
|
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="policy_bl_mailbox" name="multi_select" value="' + item.prefid + '" />';
|
||||||
|
|||||||
@@ -324,6 +324,9 @@ if (isset($_GET['query'])) {
|
|||||||
case "app-passwd":
|
case "app-passwd":
|
||||||
process_add_return(app_passwd('add', $attr));
|
process_add_return(app_passwd('add', $attr));
|
||||||
break;
|
break;
|
||||||
|
case "mta-sts":
|
||||||
|
process_add_return(mailbox('add', 'mta_sts', $attr));
|
||||||
|
break;
|
||||||
// return no route found if no case is matched
|
// return no route found if no case is matched
|
||||||
default:
|
default:
|
||||||
http_response_code(404);
|
http_response_code(404);
|
||||||
@@ -1976,6 +1979,9 @@ if (isset($_GET['query'])) {
|
|||||||
case "ip_check":
|
case "ip_check":
|
||||||
process_edit_return(customize('edit', 'ip_check', $attr));
|
process_edit_return(customize('edit', 'ip_check', $attr));
|
||||||
break;
|
break;
|
||||||
|
case "custom_login":
|
||||||
|
process_edit_return(customize('edit', 'custom_login', $attr));
|
||||||
|
break;
|
||||||
case "self":
|
case "self":
|
||||||
if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
||||||
process_edit_return(domain_admin('edit', $attr));
|
process_edit_return(domain_admin('edit', $attr));
|
||||||
@@ -1986,6 +1992,7 @@ if (isset($_GET['query'])) {
|
|||||||
break;
|
break;
|
||||||
case "cors":
|
case "cors":
|
||||||
process_edit_return(cors('edit', $attr));
|
process_edit_return(cors('edit', $attr));
|
||||||
|
break;
|
||||||
case "identity-provider":
|
case "identity-provider":
|
||||||
process_edit_return(identity_provider('edit', $attr));
|
process_edit_return(identity_provider('edit', $attr));
|
||||||
break;
|
break;
|
||||||
@@ -1998,6 +2005,9 @@ if (isset($_GET['query'])) {
|
|||||||
case "reset-password-notification":
|
case "reset-password-notification":
|
||||||
process_edit_return(reset_password('edit_notification', $attr));
|
process_edit_return(reset_password('edit_notification', $attr));
|
||||||
break;
|
break;
|
||||||
|
case "mta-sts":
|
||||||
|
process_edit_return(mailbox('edit', 'mta_sts', array_merge(array('domains' => $items), $attr)));
|
||||||
|
break;
|
||||||
// return no route found if no case is matched
|
// return no route found if no case is matched
|
||||||
default:
|
default:
|
||||||
http_response_code(404);
|
http_response_code(404);
|
||||||
|
|||||||
1391
data/web/lang/lang.bg-bg.json
Normal file
1391
data/web/lang/lang.bg-bg.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,16 @@
|
|||||||
"spam_alias": "Àlies temporals",
|
"spam_alias": "Àlies temporals",
|
||||||
"spam_score": "Puntuació de correu brossa",
|
"spam_score": "Puntuació de correu brossa",
|
||||||
"tls_policy": "Política TLS",
|
"tls_policy": "Política TLS",
|
||||||
"unlimited_quota": "Quota ilimitada per bústies de correo"
|
"unlimited_quota": "Quota ilimitada per bústies de correo",
|
||||||
|
"delimiter_action": "Acció delimitadora",
|
||||||
|
"domain_relayhost": "Canviar relayhost per un domini",
|
||||||
|
"extend_sender_acl": "Permetre extendre l'ACL del remitent per adreces externes",
|
||||||
|
"mailbox_relayhost": "Canvia el host de reenviament per una bústia",
|
||||||
|
"pushover": "Pushover",
|
||||||
|
"pw_reset": "Permetre el restabliment de la contrasenya de l'usuari mailcow",
|
||||||
|
"ratelimit": "Límit de peticions",
|
||||||
|
"smtp_ip_access": "Canvia hosts permesos per SMTP",
|
||||||
|
"sogo_access": "Permetre la gestió d'accés a SOGo"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
"activate_filter_warn": "All other filters will be deactivated, when active is checked.",
|
"activate_filter_warn": "All other filters will be deactivated, when active is checked.",
|
||||||
@@ -73,7 +82,25 @@
|
|||||||
"validate": "Validar",
|
"validate": "Validar",
|
||||||
"validation_success": "Validated successfully",
|
"validation_success": "Validated successfully",
|
||||||
"app_name": "Nom de l'aplicació",
|
"app_name": "Nom de l'aplicació",
|
||||||
"app_password": "Afegir contrasenya a l'aplicació"
|
"app_password": "Afegir contrasenya a l'aplicació",
|
||||||
|
"app_passwd_protocols": "Protocols autoritzats per la contrasenya de l'aplicació",
|
||||||
|
"bcc_dest_format": "La destinació c/o ha de ser una única adreça de correu vàlida.<br>Si necessiteu enviar una còpia a diverses adreces, creeu un àlies i utilitzeu-lo aquí.",
|
||||||
|
"comment_info": "Els comentaris privats no són visibles per l'usuari, mentre que els comentaris públics apareixen com una descripció emergent a la informació de l'usuari",
|
||||||
|
"custom_params": "Paràmetres personalitzats",
|
||||||
|
"custom_params_hint": "Correcte: --param=xy, incorrecte: --param xy",
|
||||||
|
"destination": "Destí",
|
||||||
|
"disable_login": "No permetre l'inici de sessió (els missatges entrants continuen sent acceptats)",
|
||||||
|
"domain_matches_hostname": "El domini %s coincideix amb el nom del servidor",
|
||||||
|
"dry": "Simular la sincronització",
|
||||||
|
"gal": "Llista d'adreces global",
|
||||||
|
"generate": "genereu",
|
||||||
|
"inactive": "Inactiu",
|
||||||
|
"internal": "Intern",
|
||||||
|
"internal_info": "Els àlies interns són només accessibles des del mateix domini o els àlies de dominis.",
|
||||||
|
"mailbox_quota_def": "Quota per defecte de la bústia",
|
||||||
|
"nexthop": "Següent salt",
|
||||||
|
"private_comment": "Comentari privat",
|
||||||
|
"public_comment": "Comentari púlbic"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Accés",
|
"access": "Accés",
|
||||||
@@ -557,4 +584,4 @@
|
|||||||
"week": "Setmana",
|
"week": "Setmana",
|
||||||
"weeks": "Setmanes"
|
"weeks": "Setmanes"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"acl": {
|
"acl": {
|
||||||
"alias_domains": "Doménové aliasy",
|
"alias_domains": "Doménové aliasy",
|
||||||
"app_passwds": "Hesla aplikací",
|
"app_passwds": "Správa hesel aplikací",
|
||||||
"bcc_maps": "BCC mapy",
|
"bcc_maps": "BCC mapy",
|
||||||
"delimiter_action": "Zacházení s označkovanou poštou",
|
"delimiter_action": "Zacházení s označkovanou poštou",
|
||||||
"domain_desc": "Změnit popis domény",
|
"domain_desc": "Změnit popis domény",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"sogo_access": "Správa přístupu do SOGo",
|
"sogo_access": "Správa přístupu do SOGo",
|
||||||
"sogo_profile_reset": "Resetování profilu SOGo",
|
"sogo_profile_reset": "Resetování profilu SOGo",
|
||||||
"spam_alias": "Dočasné aliasy",
|
"spam_alias": "Dočasné aliasy",
|
||||||
"spam_policy": "Blacklist/Whitelist",
|
"spam_policy": "Denylist/Allowlist",
|
||||||
"spam_score": "Skóre spamu",
|
"spam_score": "Skóre spamu",
|
||||||
"syncjobs": "Synchronizační úlohy",
|
"syncjobs": "Synchronizační úlohy",
|
||||||
"tls_policy": "Pravidla TLS",
|
"tls_policy": "Pravidla TLS",
|
||||||
@@ -82,12 +82,12 @@
|
|||||||
"password": "Heslo",
|
"password": "Heslo",
|
||||||
"password_repeat": "Potvrzení nového hesla (opakujte)",
|
"password_repeat": "Potvrzení nového hesla (opakujte)",
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"post_domain_add": "Po přidání nové domény je nutné restartovat SOGo kontejner!",
|
"post_domain_add": "Po přidání nové domény se musí restartovat kontejner SOGo!<br><br>Je také třeba ověřit nastavení DNS nové domény. Po ověření restartujte kontejner \"acme-mailcow\", aby se vygenerovaly certifikáty domény (autoconfig.<domain>, autodiscover.<domain>).<br>Tento krok je volitelný, a provede se automaticky každých 24 hodin.",
|
||||||
"private_comment": "Soukromý komentář",
|
"private_comment": "Soukromý komentář",
|
||||||
"public_comment": "Veřejný komentář",
|
"public_comment": "Veřejný komentář",
|
||||||
"quota_mb": "Kvóta (MiB)",
|
"quota_mb": "Kvóta (MiB)",
|
||||||
"relay_all": "Předávání všech příjemců",
|
"relay_all": "Předávání všech příjemců",
|
||||||
"relay_all_info": "<small>Pokud se rozhodnete <b>nepředávat</b> všechny příjemce, musíte přidat prázdnou mailovou schránku pro každého příjemce, který se má předávat.</small>",
|
"relay_all_info": "↪Pokud se rozhodnete <b>nepředávat</b> všechny příjemce, musíte přidat prázdnou mailovou schránku pro každého příjemce, který se má předávat.",
|
||||||
"relay_domain": "Předávání domény",
|
"relay_domain": "Předávání domény",
|
||||||
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> U této domény lze pro konkrétní cíl nastavit transportní mapu. Není-li nastavena, použije se MX záznam.",
|
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> U této domény lze pro konkrétní cíl nastavit transportní mapu. Není-li nastavena, použije se MX záznam.",
|
||||||
"relay_unknown_only": "Předávat jen neexistující schránky. Doručení do existujících proběhne lokálně.",
|
"relay_unknown_only": "Předávat jen neexistující schránky. Doručení do existujících proběhne lokálně.",
|
||||||
@@ -109,7 +109,9 @@
|
|||||||
"validate": "Ověřit",
|
"validate": "Ověřit",
|
||||||
"validation_success": "Úspěšně ověřeno",
|
"validation_success": "Úspěšně ověřeno",
|
||||||
"tags": "Štítky",
|
"tags": "Štítky",
|
||||||
"dry": "Simulovat synchronizaci"
|
"dry": "Simulovat synchronizaci",
|
||||||
|
"internal": "Interní",
|
||||||
|
"internal_info": "Interní aliasy jsou přístupné jen z vlastních domén nebo jejich aliasů."
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Přístupy",
|
"access": "Přístupy",
|
||||||
@@ -147,7 +149,7 @@
|
|||||||
"arrival_time": "Čas zařazení do fronty (čas na serveru)",
|
"arrival_time": "Čas zařazení do fronty (čas na serveru)",
|
||||||
"authed_user": "Přihlášený uživatel",
|
"authed_user": "Přihlášený uživatel",
|
||||||
"ays": "Opravdu chcete pokračovat?",
|
"ays": "Opravdu chcete pokračovat?",
|
||||||
"ban_list_info": "Seznam blokovaných IP adres je zobrazen níže: <b>síť (zbývající čas blokování) - [akce]</b>.<br />IP adresy zařazené pro odblokování budou z aktivního seznamu odebrány během několika sekund.<br />Červeně označené položky jsou pernamentní bloky z blacklistu.",
|
"ban_list_info": "Viz seznam zablokovaných IP níže: <b>síť (zbývající doba zablokování) - [akce]</b>.<br />IP adresy zařazené pro odblokování budou z aktivního seznamu odebrány během pár sekund.<br />Červeně označeny jsou položky z trvalých seznamů.",
|
||||||
"change_logo": "Změnit logo",
|
"change_logo": "Změnit logo",
|
||||||
"logo_normal_label": "Normální",
|
"logo_normal_label": "Normální",
|
||||||
"logo_dark_label": "Inverzní pro tmavý režim",
|
"logo_dark_label": "Inverzní pro tmavý režim",
|
||||||
@@ -181,16 +183,16 @@
|
|||||||
"empty": "Žádné výsledky",
|
"empty": "Žádné výsledky",
|
||||||
"excludes": "Vyloučit tyto příjemce",
|
"excludes": "Vyloučit tyto příjemce",
|
||||||
"f2b_ban_time": "Doba blokování (s)",
|
"f2b_ban_time": "Doba blokování (s)",
|
||||||
"f2b_blacklist": "Sítě/hostitelé na blacklistu",
|
"f2b_blacklist": "Sítě či hostitelé na seznamu zákazů",
|
||||||
"f2b_filter": "Regex filtre",
|
"f2b_filter": "Regex filtre",
|
||||||
"f2b_list_info": "Síť nebo hostitelé na blacklistu mají vždy větší váhu než položky na whitelistu. Blacklist se sestavuje vždy při startu kontejneru.",
|
"f2b_list_info": "Sítě či hostitelé na seznamu zákazů mají vždy větší váhu než položky na seznamu povolení. <b>Každá úprava seznamu trvá pár sekund.</b>",
|
||||||
"f2b_max_attempts": "Max. pokusů",
|
"f2b_max_attempts": "Max. pokusů",
|
||||||
"f2b_netban_ipv4": "Rozsah IPv4 podsítě k zablokování (8-32)",
|
"f2b_netban_ipv4": "Rozsah IPv4 podsítě k zablokování (8-32)",
|
||||||
"f2b_netban_ipv6": "Rozsah IPv6 podsítě k zablokování (8-128)",
|
"f2b_netban_ipv6": "Rozsah IPv6 podsítě k zablokování (8-128)",
|
||||||
"f2b_parameters": "Parametry automatického firewallu",
|
"f2b_parameters": "Parametry automatického firewallu",
|
||||||
"f2b_regex_info": "Záznamy které se berou v úvahu: SOGo, Postfix, Dovecot, PHP-FPM.",
|
"f2b_regex_info": "Záznamy které se berou v úvahu: SOGo, Postfix, Dovecot, PHP-FPM.",
|
||||||
"f2b_retry_window": "Časový horizont pro maximum pokusů (s)",
|
"f2b_retry_window": "Časový horizont pro maximum pokusů (s)",
|
||||||
"f2b_whitelist": "Sítě/hostitelé na whitelistu",
|
"f2b_whitelist": "Sítě či hostitelé na seznamu povolení",
|
||||||
"filter_table": "Tabulka filtrů",
|
"filter_table": "Tabulka filtrů",
|
||||||
"forwarding_hosts": "Předávající servery",
|
"forwarding_hosts": "Předávající servery",
|
||||||
"forwarding_hosts_add_hint": "Lze zadat IPv4/IPv6 adresy, sítě ve formátu CIDR, názvy serverů (budou převedeny na IP adresy) nebo názvy domén (budou převedeny na IP pomocí SPF záznamů, příp. MX záznamů).",
|
"forwarding_hosts_add_hint": "Lze zadat IPv4/IPv6 adresy, sítě ve formátu CIDR, názvy serverů (budou převedeny na IP adresy) nebo názvy domén (budou převedeny na IP pomocí SPF záznamů, příp. MX záznamů).",
|
||||||
@@ -256,7 +258,7 @@
|
|||||||
"quarantine_exclude_domains": "Vyloučené domény a doménové aliasy",
|
"quarantine_exclude_domains": "Vyloučené domény a doménové aliasy",
|
||||||
"quarantine_max_age": "Maximální stáří ve dnech<br><small>Hodnota musí být rovna nebo větší než 1 den.</small>",
|
"quarantine_max_age": "Maximální stáří ve dnech<br><small>Hodnota musí být rovna nebo větší než 1 den.</small>",
|
||||||
"quarantine_max_score": "Neposílat notifikace pokud je spam skóre větší než hodnota:<br><small>Výchozí je 9999.0</small>",
|
"quarantine_max_score": "Neposílat notifikace pokud je spam skóre větší než hodnota:<br><small>Výchozí je 9999.0</small>",
|
||||||
"quarantine_max_size": "Maximální velikost v MiB (větší prvky budou smazány)<br />0 <b>neznamená</b> neomezeno.",
|
"quarantine_max_size": "Maximální velikost v MiB (větší prvky budou smazány)<br /><small>0 <b>neznamená</b> neomezeno.</small>",
|
||||||
"quarantine_notification_html": "Šablona upozornění:<br><small>Ponechte prázdné, aby se obnovila výchozí šablona.</small>",
|
"quarantine_notification_html": "Šablona upozornění:<br><small>Ponechte prázdné, aby se obnovila výchozí šablona.</small>",
|
||||||
"quarantine_notification_sender": "Odesílatel upozornění",
|
"quarantine_notification_sender": "Odesílatel upozornění",
|
||||||
"quarantine_notification_subject": "Předmět upozornění",
|
"quarantine_notification_subject": "Předmět upozornění",
|
||||||
@@ -264,7 +266,7 @@
|
|||||||
"quarantine_release_format": "Formát propuštěných položek",
|
"quarantine_release_format": "Formát propuštěných položek",
|
||||||
"quarantine_release_format_att": "Jako příloha",
|
"quarantine_release_format_att": "Jako příloha",
|
||||||
"quarantine_release_format_raw": "Nezměněný originál",
|
"quarantine_release_format_raw": "Nezměněný originál",
|
||||||
"quarantine_retention_size": "Počet zadržených zpráv na mailovou schránku<br />0 znamená <b>neaktivní</b>.",
|
"quarantine_retention_size": "Počet zadržených zpráv na mailovou schránku<br /><small>0 znamená <b>neaktivní</b>.</small>",
|
||||||
"quota_notification_html": "Šablona upozornění:<br><small>Ponechte prázdné, aby se obnovila výchozí šablona.</small>",
|
"quota_notification_html": "Šablona upozornění:<br><small>Ponechte prázdné, aby se obnovila výchozí šablona.</small>",
|
||||||
"quota_notification_sender": "Odesílatel upozornění",
|
"quota_notification_sender": "Odesílatel upozornění",
|
||||||
"quota_notification_subject": "Předmět upozornění",
|
"quota_notification_subject": "Předmět upozornění",
|
||||||
@@ -283,7 +285,7 @@
|
|||||||
"relay_rcpt": "\"Komu:\" adresa",
|
"relay_rcpt": "\"Komu:\" adresa",
|
||||||
"relay_run": "Provést test",
|
"relay_run": "Provést test",
|
||||||
"relayhosts": "Transporty podle odesílatele",
|
"relayhosts": "Transporty podle odesílatele",
|
||||||
"relayhosts_hint": "Zde definujte transporty podle odesílatele, jež pak můžete použít v nastavení domény.<br>\r\nProtokol transportu je vždy \"smtp:\". Bere se v potaz uživatelské nastavení odchozího TLS.",
|
"relayhosts_hint": "Zde definujte transporty podle odesílatele, jež pak můžete použít v nastavení domény.<br>\nProtokol transportu je vždy \"smtp:\" a použije se TLS, je-li nabídnuto. Zabalené TLS (SMTPS) se nepodporuje. Bere se v potaz uživatelské nastavení odchozího TLS.<br>\nTýká se vybraných domén včetně doménových aliasů.",
|
||||||
"remove": "Smazat",
|
"remove": "Smazat",
|
||||||
"remove_row": "Smazat řádek",
|
"remove_row": "Smazat řádek",
|
||||||
"reset_default": "Obnovit výchozí nastavení",
|
"reset_default": "Obnovit výchozí nastavení",
|
||||||
@@ -299,11 +301,11 @@
|
|||||||
"rsettings_preset_2": "Postmasteři chtějí dostávat spam",
|
"rsettings_preset_2": "Postmasteři chtějí dostávat spam",
|
||||||
"rsettings_preset_3": "Povolit jen určité odesílatele pro schránku (např. jen interní schránka)",
|
"rsettings_preset_3": "Povolit jen určité odesílatele pro schránku (např. jen interní schránka)",
|
||||||
"rsettings_preset_4": "Deaktivujte Rspamd pro doménu",
|
"rsettings_preset_4": "Deaktivujte Rspamd pro doménu",
|
||||||
"rspamd_com_settings": "<a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd dokumentace</a>\r\n - Název nastavení bude automaticky vygenerován, viz níže uvedené předvolby.",
|
"rspamd_com_settings": "Název nastavení se vygeneruje automaticky, viz ukázky nastavení níže. Více informací viz <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd dokumentace</a>",
|
||||||
"rspamd_global_filters": "Mapa globálních filtrů",
|
"rspamd_global_filters": "Mapa globálních filtrů",
|
||||||
"rspamd_global_filters_agree": "Budu opatrný!",
|
"rspamd_global_filters_agree": "Budu opatrný!",
|
||||||
"rspamd_global_filters_info": "Mapa globálních filtrů obsahuje jiné globální black- a whitelisty.",
|
"rspamd_global_filters_info": "Mapa globálních filtrů obsahuje různé seznamy povolených a zakázaných serverů",
|
||||||
"rspamd_global_filters_regex": "Názvy jsou dostatečným vysvětlením. Musí obsahovat jen platné regulární výrazy ve formátu \"/vyraz/parametry\" (e.g. <code>/.+@domena\\.tld/i</code>).<br>\r\n Každý výraz bude podroben základní kontrole, přesto je možné Rspamd 'rozbít', nebude-li syntax zcela korektní.<br>\r\n Rspamd se pokusí načíst mapu po každé změně. V případě potíží, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">restartujte Rspamd</a>, aby se konfigurace načetla explicitně.",
|
"rspamd_global_filters_regex": "Názvy stačí k vysvětlení. Položky musejí obsahovat jen platné regulární výrazy ve tvaru \"/vyraz/parametry\" (e.g. <code>/.+@domena\\.tld/i</code>).<br>\n Každý výraz bude podroben základní kontrole, přesto je možné Rspamd 'rozbít', nebude-li syntax zcela korektní.<br>\n Rspamd se pokusí po každé změně načíst mapu znovu. V případě potíží <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">restartujte Rspamd</a>, aby se konfigurace načetla explicitně.",
|
||||||
"rspamd_settings_map": "Nastavení Rspamd",
|
"rspamd_settings_map": "Nastavení Rspamd",
|
||||||
"sal_level": "Úroveň 'Moo'",
|
"sal_level": "Úroveň 'Moo'",
|
||||||
"save": "Uložit změny",
|
"save": "Uložit změny",
|
||||||
@@ -324,8 +326,8 @@
|
|||||||
"to_top": "Zpět na začátek",
|
"to_top": "Zpět na začátek",
|
||||||
"transport_dest_format": "Formát: example.org, .example.org, *, box@example.org (vícero položek lze oddělit čárkou)",
|
"transport_dest_format": "Formát: example.org, .example.org, *, box@example.org (vícero položek lze oddělit čárkou)",
|
||||||
"transport_maps": "Transportní mapy",
|
"transport_maps": "Transportní mapy",
|
||||||
"transport_test_rcpt_info": "• Na otestování odchozí pošty je možné použít null@hosted.mailcow.de jako adresáta",
|
"transport_test_rcpt_info": "• K otestování předávání pošty ven použijte null@hosted.mailcow.de.",
|
||||||
"transports_hint": "→ Položka transportní mapy <b>přebíjí</b> transportní mapu podle odesílatele</b>.<br>\r\n→ Uživatelské nastavení odchozího TLS se ignoruje a lze je výhradně vynutit mapováním TLS pravidel.<br>\r\n→ Protokol transportu je vždy \"smtp:\".<br>\r\n→ Adresy, jež odpovídají výrazu \"/localhost$/\", se vždy předají přes \"local:\", takže nejsou zahrnuty do definice cíle \"*\".<br>\r\n→ Pro stanovení přihlašovacích údajů dalšího skoku, např. \"[host]:25\", bude Postfix <b>vždy</b> hledat nejdříve \"host\" a teprve pak \"[host]:25\". Kvůli tomu nelze použít současně \"host\" a \"[host]:25\"",
|
"transports_hint": "• Položka transportní mapy <b>přebíjí</b> transportní mapu podle odesílatele</b>.<br>\n• Transporty založené na MX mají přednost.<br>\n• Uživatelské nastavení odchozího TLS se ignoruje a lze je vynutit výhradně mapou TLS pravidel.<br>\n• Transportní služnou pro tyto transporty je vždy \"smtp:\" a použije se TLS, je-li nabídnuto. Zabalené TLS (SMTPS) se nepodporuje.<br>\n• Adresy, jež odpovídají výrazu \"/localhost$/\", se vždy předají přes \"local:\", takže nejsou zahrnuty do definice cíle \"*\".<br>\n• Pro stanovení přihlašovacích údajů dalšího skoku, např. \"[host]:25\", bude Postfix <b>vždy</b> hledat nejdříve \"host\" a teprve pak \"[host]:25\". Kvůli tomu nelze použít současně \"host\" a \"[host]:25\".",
|
||||||
"ui_footer": "Pata stránka (HTML povoleno)",
|
"ui_footer": "Pata stránka (HTML povoleno)",
|
||||||
"ui_header_announcement": "Oznámení",
|
"ui_header_announcement": "Oznámení",
|
||||||
"ui_header_announcement_active": "Nastavit jako aktivní",
|
"ui_header_announcement_active": "Nastavit jako aktivní",
|
||||||
@@ -344,17 +346,72 @@
|
|||||||
"validate_license_now": "Ověřit GUID na licenčním serveru",
|
"validate_license_now": "Ověřit GUID na licenčním serveru",
|
||||||
"verify": "Ověřit",
|
"verify": "Ověřit",
|
||||||
"yes": "✓",
|
"yes": "✓",
|
||||||
"f2b_ban_time_increment": "Délka banu je prodlužována s každým dalším banem",
|
"f2b_ban_time_increment": "Délka bloku se prodlužuje s každým dalším zablokováním",
|
||||||
"f2b_max_ban_time": "Maximální délka banu (s)",
|
"f2b_max_ban_time": "Maximální délka bloku (s)",
|
||||||
"cors_settings": "Nastavení CORS",
|
"cors_settings": "Nastavení CORS",
|
||||||
"queue_unban": "zrušit ban",
|
"queue_unban": "odblokovat",
|
||||||
"password_reset_info": "Pokud není zadán žádný e-mail pro obnovení, nelze tuto funkci použít.",
|
"password_reset_info": "Pokud není zadán žádný e-mail pro obnovení, nelze tuto funkci použít.",
|
||||||
"password_reset_settings": "Nastavení obnovení hesla",
|
"password_reset_settings": "Nastavení obnovení hesla",
|
||||||
"password_settings": "Nastavení hesel",
|
"password_settings": "Nastavení hesel",
|
||||||
"password_reset_tmpl_html": "HTML šablona",
|
"password_reset_tmpl_html": "HTML šablona",
|
||||||
"password_reset_tmpl_text": "Textová šablona",
|
"password_reset_tmpl_text": "Textová šablona",
|
||||||
"reset_password_vars": "<code>{{link}}</code> Vygenerovaný odkaz pro obnovení hesla<br><code>{{username}}</code> Název mailboxu uživatele, který požádal o resetování hesla.<br><code>{{username2}}</code> Název schránky pro obnovení<br><code>{{date}}</code> Datum podání žádosti o obnovení hesla<br><code>{{token_lifetime}}</code> Délka životnosti tokenu v minutách<br><code>{{hostname}}</code> Název serveru mailcow",
|
"reset_password_vars": "<code>{{link}}</code> Vygenerovaný odkaz pro obnovení hesla<br><code>{{username}}</code> Název mailboxu uživatele, který požádal o resetování hesla.<br><code>{{username2}}</code> Název schránky pro obnovení<br><code>{{date}}</code> Datum podání žádosti o obnovení hesla<br><code>{{token_lifetime}}</code> Délka životnosti tokenu v minutách<br><code>{{hostname}}</code> Název serveru mailcow",
|
||||||
"restore_template": "Ponechte prázdné pro obnovení výchozí šablony."
|
"restore_template": "Ponechte prázdné pro obnovení výchozí šablony.",
|
||||||
|
"copy_to_clipboard": "Text zkopírován do schránky!",
|
||||||
|
"iam_login_provisioning": "Automaticky vytvořit uživatele při přihlášení",
|
||||||
|
"user_quicklink": "Skrýt zkratku na přihlášení uživatele",
|
||||||
|
"domainadmin_quicklink": "Skrýt zkratku na přihlášení správce domény",
|
||||||
|
"iam_auth_flow_info": "Kromě metody autorizačního kódu (Authorization Code Flow, výchozího v Keycloaku), jež se používá pro SSO, podporuje mailcow také metody autentizaci přímo pomocí přihlašovacích údajů. The metoda Mailpassword Flow se pokusí ověřit přihlašovací údaje uživatele přímo v Admin REST API Keycloaku. mailcow získá hash hesla z atributu <code>mailcow_password</code> , namapovaného v Keycloaku.",
|
||||||
|
"iam_default_template_description": "Nemá-li uživatel přiřazenu šablonu, použije se výchozí šablona k vytvoření schránky, ale ne k její úpravě či aktualizaci.",
|
||||||
|
"iam_userinfo_url": "Koncový bod pro informace o uživatelích",
|
||||||
|
"iam_redirect_url": "URL přesměrování",
|
||||||
|
"user_link": "Odkaz pro uživatele",
|
||||||
|
"force_sso_text": "Je-li nastaven externí poskytovatel OIDC, zapnutím této volby skryjete výchozí přihlašovací formulář. Zůstane vidět jen tlačítko pro SSO",
|
||||||
|
"iam_mapping": "Mapování atributů",
|
||||||
|
"iam_bindpass": "Heslo pro bind",
|
||||||
|
"iam_periodic_full_sync": "Pravidelná úplná synchronizace",
|
||||||
|
"iam_port": "Port",
|
||||||
|
"iam_realm": "Realm",
|
||||||
|
"iam_rest_flow": "Mailpassword Flow",
|
||||||
|
"iam_server_url": "URL serveru",
|
||||||
|
"iam_sso": "Single Sign-On",
|
||||||
|
"iam_sync_interval": "Interval synchronizace/importu (min)",
|
||||||
|
"iam_test_connection": "Test spojení",
|
||||||
|
"iam_token_url": "Koncový bod pro tokeny",
|
||||||
|
"iam_username_field": "Pole uživatelského jména",
|
||||||
|
"iam_binddn": "Doména pro bind",
|
||||||
|
"iam_use_ssl": "Používat SSL",
|
||||||
|
"iam_use_tls": "Používat StartTLS",
|
||||||
|
"iam_version": "Verze",
|
||||||
|
"quicklink_text": "Zobrazení zkratek k dalším přihlašovacím stránkám",
|
||||||
|
"iam_use_tls_info": "Je-li zapnuto TLS, musí se používat standardní port pro LDAP (389). Port SSL nelze použít.",
|
||||||
|
"ignore_ssl_error": "Ignorovat chyby SSL",
|
||||||
|
"iam_use_ssl_info": "Je-li zapnuto SSL a nastaven port 389, použije se automaticky port 636.",
|
||||||
|
"task": "Úloha",
|
||||||
|
"app_hide": "Skrýt při přihlášení",
|
||||||
|
"admin_quicklink": "Skrýt zkratku na přihlášení správce",
|
||||||
|
"allowed_methods": "Access-Control-Allow-Methods",
|
||||||
|
"allowed_origins": "Access-Control-Allow-Origin",
|
||||||
|
"login_page": "Přihlašovací stránka",
|
||||||
|
"f2b_manage_external": "Spravovat Fail2Ban externě",
|
||||||
|
"f2b_manage_external_info": "Fail2ban bude udržovat seznam zakázaných adres, ale nebude aktivně nastavovat pravidla blokování. Pro blokování použijte seznam adres níže.",
|
||||||
|
"filter": "Filtr",
|
||||||
|
"force_sso": "Vypnout přihlášení mailcow a ponechat jen SSO",
|
||||||
|
"iam": "Poskytovatel identity",
|
||||||
|
"iam_attribute_field": "Pole atributu",
|
||||||
|
"iam_authorize_url": "Autorizační koncový bod",
|
||||||
|
"iam_basedn": "Doména (Base DN)",
|
||||||
|
"iam_client_id": "ID klienta",
|
||||||
|
"iam_client_secret": "Tajný kód klienta",
|
||||||
|
"iam_client_scopes": "Scopes klienta",
|
||||||
|
"iam_default_template": "Výchozí šablona",
|
||||||
|
"iam_description": "Nastavení externího poskytovatele ověření<br>Schránky uživatele se vytvoří po prvním přihlášení automaticky, pokud je tedy nastaveno mapování atributů.",
|
||||||
|
"iam_extra_permission": "Aby vše fungovalo, musí mít mailcow klient v Keycloaku nastavený <code>servisní účet</code> a povolení <code>view-users</code>.",
|
||||||
|
"iam_host": "Hostitel",
|
||||||
|
"iam_host_info": "Zadejte jeden či více hostitelů, oddělte čárkou.",
|
||||||
|
"iam_import_users": "Importovat uživatele",
|
||||||
|
"iam_auth_flow": "Proces autentizace",
|
||||||
|
"needs_restart": "potřebuje restart"
|
||||||
},
|
},
|
||||||
"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",
|
||||||
@@ -408,7 +465,7 @@
|
|||||||
"is_alias": "%s je již známa jako adresa aliasu",
|
"is_alias": "%s je již známa jako adresa aliasu",
|
||||||
"is_alias_or_mailbox": "%s je již známa jako adresa aliasu, mailové schránky nebo aliasu rozvedeného z aliasu domény.",
|
"is_alias_or_mailbox": "%s je již známa jako adresa aliasu, mailové schránky nebo aliasu rozvedeného z aliasu domény.",
|
||||||
"is_spam_alias": "%s je již známa jako adresa spamového aliasu",
|
"is_spam_alias": "%s je již známa jako adresa spamového aliasu",
|
||||||
"last_key": "Nelze smazat poslední klíč",
|
"last_key": "Nelze smazat poslední klíč, vypněte tedy celé TFA.",
|
||||||
"login_failed": "Přihlášení selhalo",
|
"login_failed": "Přihlášení selhalo",
|
||||||
"mailbox_defquota_exceeds_mailbox_maxquota": "Výchozí kvóta překračuje maximální kvótu schránky\"",
|
"mailbox_defquota_exceeds_mailbox_maxquota": "Výchozí kvóta překračuje maximální kvótu schránky\"",
|
||||||
"mailbox_invalid": "Název mailové schránky je neplatný",
|
"mailbox_invalid": "Název mailové schránky je neplatný",
|
||||||
@@ -486,7 +543,20 @@
|
|||||||
"demo_mode_enabled": "Demo režim je zapnutý",
|
"demo_mode_enabled": "Demo režim je zapnutý",
|
||||||
"recovery_email_failed": "Nepodařilo se odeslat e-mail pro obnovení. Obraťte se prosím na svého správce.",
|
"recovery_email_failed": "Nepodařilo se odeslat e-mail pro obnovení. Obraťte se prosím na svého správce.",
|
||||||
"password_reset_invalid_user": "Mailbox nebyl nalezen nebo není nastaven žádný e-mail pro obnovu",
|
"password_reset_invalid_user": "Mailbox nebyl nalezen nebo není nastaven žádný e-mail pro obnovu",
|
||||||
"password_reset_na": "Obnovení hesla není v současné době k dispozici. Obraťte se prosím na svého správce."
|
"password_reset_na": "Obnovení hesla není v současné době k dispozici. Obraťte se prosím na svého správce.",
|
||||||
|
"generic_server_error": "Došlo k nečekané chybě. Obraťte se na vašeho správce.",
|
||||||
|
"to_invalid": "Adresát nemůže být prázdný",
|
||||||
|
"authsource_in_use": "Poskytovatele identity nelze změnit nebo odstranit, neboť se právě používá pro jednoho či více uživatelů.",
|
||||||
|
"iam_test_connection": "Spojení selhalo",
|
||||||
|
"img_dimensions_exceeded": "Obrázek je větší než povolené rozměry",
|
||||||
|
"img_size_exceeded": "Obrázek má větší než povolenou velikost souboru",
|
||||||
|
"invalid_reset_token": "Neplatný resetovací token",
|
||||||
|
"required_data_missing": "Chybí potřebný údaj %s",
|
||||||
|
"reset_token_limit_exceeded": "Byl překročen limit na reset tokeny. Zkuste to později.",
|
||||||
|
"max_age_invalid": "Maximální životnost %s není platná",
|
||||||
|
"mode_invalid": "Mód %s není platný",
|
||||||
|
"mx_invalid": "Záznam MX %s není platný",
|
||||||
|
"version_invalid": "Verze %s není platná"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
"emptyTable": "Tabulka neobsahuje žádná data",
|
"emptyTable": "Tabulka neobsahuje žádná data",
|
||||||
@@ -548,7 +618,8 @@
|
|||||||
"update_failed": "Nepodařilo se zkontrolovat aktualizace",
|
"update_failed": "Nepodařilo se zkontrolovat aktualizace",
|
||||||
"wip": "Nedokončená vývojová verze",
|
"wip": "Nedokončená vývojová verze",
|
||||||
"memory": "Paměť",
|
"memory": "Paměť",
|
||||||
"container_disabled": "Kontejner je zastaven nebo zakázán"
|
"container_disabled": "Kontejner je zastaven nebo zakázán",
|
||||||
|
"cores": "jádra"
|
||||||
},
|
},
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"cname_from_a": "Hodnota odvozena z A/AAAA záznamu. Lze použít, pokud záznam ukazuje na správný zdroj.",
|
"cname_from_a": "Hodnota odvozena z A/AAAA záznamu. Lze použít, pokud záznam ukazuje na správný zdroj.",
|
||||||
@@ -569,7 +640,7 @@
|
|||||||
"alias": "Upravit alias",
|
"alias": "Upravit alias",
|
||||||
"allow_from_smtp": "Umožnit pouze těmto IP adresám používat <b>SMTP</b>",
|
"allow_from_smtp": "Umožnit pouze těmto IP adresám používat <b>SMTP</b>",
|
||||||
"allow_from_smtp_info": "Nechte prázdné pro povolení všech odesílatelů.<br>IPv4/IPv6 adresy a sítě.",
|
"allow_from_smtp_info": "Nechte prázdné pro povolení všech odesílatelů.<br>IPv4/IPv6 adresy a sítě.",
|
||||||
"allowed_protocols": "Povolené protokoly",
|
"allowed_protocols": "Povolené protokoly pro přímá spojení (netýká se protokolů na změnu hesla)",
|
||||||
"app_name": "Název aplikace",
|
"app_name": "Název aplikace",
|
||||||
"app_passwd": "Heslo aplikace",
|
"app_passwd": "Heslo aplikace",
|
||||||
"app_passwd_protocols": "Povolené protokoly pro hesla aplikací",
|
"app_passwd_protocols": "Povolené protokoly pro hesla aplikací",
|
||||||
@@ -607,7 +678,7 @@
|
|||||||
"inactive": "Neaktivní",
|
"inactive": "Neaktivní",
|
||||||
"kind": "Druh",
|
"kind": "Druh",
|
||||||
"last_modified": "Naposledy změněn",
|
"last_modified": "Naposledy změněn",
|
||||||
"lookup_mx": "Cíl je regulární výraz který se shoduje s MX záznamem (<code>.*\\.google\\.com</code> směřuje veškerou poštu na MX které jsou cílem pro google.com přes tento skok)",
|
"lookup_mx": "Cíl je regulární výraz, jenž se porovná s MX záznamem (např. <code>.*\\.google\\.com</code> na tento skok nasměruje veškerou poštu s MX, jež končí na *.google.com)",
|
||||||
"mailbox": "Úprava mailové schránky",
|
"mailbox": "Úprava mailové schránky",
|
||||||
"mailbox_quota_def": "Výchozí kvóta schránky",
|
"mailbox_quota_def": "Výchozí kvóta schránky",
|
||||||
"mailbox_relayhost_info": "Aplikované jen na uživatelskou schránku a přímé aliasy, přepisuje předávající server domény.",
|
"mailbox_relayhost_info": "Aplikované jen na uživatelskou schránku a přímé aliasy, přepisuje předávající server domény.",
|
||||||
@@ -641,7 +712,7 @@
|
|||||||
"ratelimit": "Omezení přenosu",
|
"ratelimit": "Omezení přenosu",
|
||||||
"redirect_uri": "URL přesměrování/odvolání",
|
"redirect_uri": "URL přesměrování/odvolání",
|
||||||
"relay_all": "Předávání všech příjemců",
|
"relay_all": "Předávání všech příjemců",
|
||||||
"relay_all_info": "<small>Pokud se rozhodnete <b>nepředávat</b> všechny příjemce, musíte přidat prázdnou mailovou schránku pro každého příjemce, který se má předávat.</small>",
|
"relay_all_info": "↪ Pokud se rozhodnete <b>nepředávat</b> všechny příjemce, musíte přidat prázdnou mailovou schránku pro každého příjemce, který se má předávat.",
|
||||||
"relay_domain": "Předávání domény",
|
"relay_domain": "Předávání domény",
|
||||||
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> U této domény lze pro konkrétní cíl nastavit transportní mapu. Není-li nastavena, použije se MX záznam.",
|
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> U této domény lze pro konkrétní cíl nastavit transportní mapu. Není-li nastavena, použije se MX záznam.",
|
||||||
"relay_unknown_only": "Předávat jen neexistující schránky. Doručení do existujících proběhne lokálně.",
|
"relay_unknown_only": "Předávat jen neexistující schránky. Doručení do existujících proběhne lokálně.",
|
||||||
@@ -662,11 +733,11 @@
|
|||||||
"sogo_visible_info": "Tato volba určuje objekty, jež lze zobrazit v SOGo (sdílené nebo nesdílené aliasy, jež ukazuje alespoň na jednu schránku).",
|
"sogo_visible_info": "Tato volba určuje objekty, jež lze zobrazit v SOGo (sdílené nebo nesdílené aliasy, jež ukazuje alespoň na jednu schránku).",
|
||||||
"spam_alias": "Vytvořit nebo změnit dočasné aliasy",
|
"spam_alias": "Vytvořit nebo změnit dočasné aliasy",
|
||||||
"spam_filter": "Spam filtr",
|
"spam_filter": "Spam filtr",
|
||||||
"spam_policy": "Přidat nebo odebrat položky whitelistu/blacklistu",
|
"spam_policy": "Přidat nebo odebrat položky seznamu",
|
||||||
"spam_score": "Nastavte vlastní skóre spamu",
|
"spam_score": "Nastavte vlastní skóre spamu",
|
||||||
"subfolder2": "Synchronizace do podsložky v cílovém umístění<br><small>(prázdné = nepoužívat podsložku)</small>",
|
"subfolder2": "Synchronizace do podsložky v cílovém umístění<br><small>(prázdné = nepoužívat podsložku)</small>",
|
||||||
"syncjob": "Upravit synchronizační úlohu",
|
"syncjob": "Upravit synchronizační úlohu",
|
||||||
"target_address": "Cílová adresa/y<br /> <small>(oddělte čárkou)</small>",
|
"target_address": "Cílová adresa/y <small>(oddělte čárkou)</small>",
|
||||||
"target_domain": "Cílová doména",
|
"target_domain": "Cílová doména",
|
||||||
"timeout1": "Časový limit pro připojení ke vzdálenému serveru",
|
"timeout1": "Časový limit pro připojení ke vzdálenému serveru",
|
||||||
"timeout2": "Časový limit pro připojení k lokálnímu serveru",
|
"timeout2": "Časový limit pro připojení k lokálnímu serveru",
|
||||||
@@ -690,7 +761,26 @@
|
|||||||
"custom_attributes": "Vlastní atributy",
|
"custom_attributes": "Vlastní atributy",
|
||||||
"footer_exclude": "Vyloučit ze zápatí",
|
"footer_exclude": "Vyloučit ze zápatí",
|
||||||
"domain_footer_skip_replies": "Ignorovat patičku u odpovědí na e-maily",
|
"domain_footer_skip_replies": "Ignorovat patičku u odpovědí na e-maily",
|
||||||
"password_recovery_email": "E-mail pro obnovu hesla"
|
"password_recovery_email": "E-mail pro obnovu hesla",
|
||||||
|
"mailbox_rename": "Přejmenovat schránku",
|
||||||
|
"mailbox_rename_agree": "Mám vytvořenou zálohu.",
|
||||||
|
"mailbox_rename_warning": "DŮLEŽITÉ! Vytvořte si zálohu schránky, než ji přejmenujete.",
|
||||||
|
"mailbox_rename_alias": "Automaticky vytvořit alias",
|
||||||
|
"mailbox_rename_title": "Nový název zdejší schránky",
|
||||||
|
"pushover": "Pushover",
|
||||||
|
"internal": "Interní",
|
||||||
|
"internal_info": "Interní aliasy jsou přístupné jen z vlastních domén nebo jejich aliasů.",
|
||||||
|
"mta_sts": "MTA-STS",
|
||||||
|
"mta_sts_info": "<a href='https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security' target='_blank'>MTA-STS</a> je standard, jenž říká poštovním serverům, aby komunikovaly pomocí TLS s platnými certifikáty. <br>Používá se, pokud není k dispozici <a target='_blank' href='https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities'>DANE</a>, např. chybí-li či není podporováno DNSSEC.<br><b>Pozn.</b>: Podporuje-li přijímající doména DANE a DNSSEC, bude <b>vždy</b> použito DANE; MTA-STS zůstane jako plán B.",
|
||||||
|
"mta_sts_version": "Verze",
|
||||||
|
"mta_sts_version_info": "Určuje verzi standardu MTA-STS – zatím je podporována jen <code>STSv1</code>.",
|
||||||
|
"mta_sts_mode": "Mód",
|
||||||
|
"mta_sts_mode_info": "K dispozici jsou tři módy:<ul><li><em>testing</em> – pravidlo se jen sleduje, porušení je bez následků.</li><li><em>enforce</em> – pravidlo je důsledně dodržováno, spojení bez platného TLS jsou odmítána.</li><li><em>none</em> – pravidlo je zveřejněno, ale neuplatňuje se.</li></ul>",
|
||||||
|
"mta_sts_max_age": "Maximální životnost",
|
||||||
|
"mta_sts_max_age_info": "Doba v sekundách, po niž poštovní servery mohou toho pravidlo držet v mezipaměti bez nutnosti obnovení.",
|
||||||
|
"mta_sts_mx": "Server MX",
|
||||||
|
"mta_sts_mx_info": "Dovoluje odesílání jen výslovně vypsaným poštovním serverům; odesílající server kontroluje, že server MX určený v DNS odpovídá pravidlu, a povolí doručení jen s platným certifikátem TLS (chrání přes útokem typu MITM).",
|
||||||
|
"mta_sts_mx_notice": "Lze zadat více serverů MX (oddělte čárkou)."
|
||||||
},
|
},
|
||||||
"fido2": {
|
"fido2": {
|
||||||
"confirm": "Potvrdit",
|
"confirm": "Potvrdit",
|
||||||
@@ -711,7 +801,7 @@
|
|||||||
"cancel": "Zrušit",
|
"cancel": "Zrušit",
|
||||||
"confirm_delete": "Potvdit smazání",
|
"confirm_delete": "Potvdit smazání",
|
||||||
"delete_now": "Smazat",
|
"delete_now": "Smazat",
|
||||||
"delete_these_items": "Prosím potvrďte změny objektu id:",
|
"delete_these_items": "Prosím potvrďte změny objektu id",
|
||||||
"hibp_check": "Ověřit heslo v databázi hacknutých hesel haveibeenpwned.com",
|
"hibp_check": "Ověřit heslo v databázi hacknutých hesel haveibeenpwned.com",
|
||||||
"hibp_nok": "Nalezeno! Toto je potenciálně nebezpečné heslo!",
|
"hibp_nok": "Nalezeno! Toto je potenciálně nebezpečné heslo!",
|
||||||
"hibp_ok": "Nebyla nalezena žádná shoda.",
|
"hibp_ok": "Nebyla nalezena žádná shoda.",
|
||||||
@@ -720,12 +810,12 @@
|
|||||||
"restart_container": "Restartovat kontejner",
|
"restart_container": "Restartovat kontejner",
|
||||||
"restart_container_info": "<b>Důležité:</b> Šetrný restart může chvíli trvat, prosím čekejte...",
|
"restart_container_info": "<b>Důležité:</b> Šetrný restart může chvíli trvat, prosím čekejte...",
|
||||||
"restart_now": "Restartovat nyní",
|
"restart_now": "Restartovat nyní",
|
||||||
"restarting_container": "Restartuje se kontejner, může to chvilku trvat..."
|
"restarting_container": "Restartuje se kontejner, může to chvilku trvat"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"administration": "Hlavní nastavení",
|
"administration": "Hlavní nastavení",
|
||||||
"apps": "Aplikace",
|
"apps": "Aplikace",
|
||||||
"debug": "Systémové informace",
|
"debug": "Informace",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
"mailcow_system": "Systém",
|
"mailcow_system": "Systém",
|
||||||
"mailcow_config": "Nastavení",
|
"mailcow_config": "Nastavení",
|
||||||
@@ -753,7 +843,15 @@
|
|||||||
"new_password": "Nové heslo",
|
"new_password": "Nové heslo",
|
||||||
"new_password_confirm": "Ověření nového hesla",
|
"new_password_confirm": "Ověření nového hesla",
|
||||||
"reset_password": "Obnovit heslo",
|
"reset_password": "Obnovit heslo",
|
||||||
"request_reset_password": "Požádat o změnu hesla"
|
"request_reset_password": "Požádat o změnu hesla",
|
||||||
|
"login_domainadmintext": "Přihlášení správce domény",
|
||||||
|
"login_linkstext": "Hledáte jinou přihlašovací stránku?",
|
||||||
|
"login_usertext": "Přihlášení uživatele",
|
||||||
|
"login_admintext": "Přihlášení správce",
|
||||||
|
"login_user": "Přihlášení uživatele",
|
||||||
|
"login_dadmin": "Přihlášení správce domény",
|
||||||
|
"login_admin": "Přihlášení správce",
|
||||||
|
"email": "Mailová adresa"
|
||||||
},
|
},
|
||||||
"mailbox": {
|
"mailbox": {
|
||||||
"action": "Akce",
|
"action": "Akce",
|
||||||
@@ -785,7 +883,7 @@
|
|||||||
"bcc": "BCC",
|
"bcc": "BCC",
|
||||||
"bcc_destination": "Cíl kopie",
|
"bcc_destination": "Cíl kopie",
|
||||||
"bcc_destinations": "Cíl kopií",
|
"bcc_destinations": "Cíl kopií",
|
||||||
"bcc_info": "Skryté kopie (Mapa BCC) se používá pro tiché předávání kopií všech zpráv na jinou adresu. Při použití skryté kopie typu <i>Přijatý e-mail</i> budou přeposlány všechny maily směřující na dotyčnou adresu nebo doménu.\nU typu <i>Odeslaný e-mail</i> budou přeposlány všechny maily odeslané z dotyčné adresy nebo domény.\nPokud selže přeposlání na cílovou adresu, tak odesílatel o tom nebude informován.",
|
"bcc_info": "Skrytá kopie (mapa BCC) se používá pro tiché předávání kopií všech zpráv na jinou adresu. Mapa příjemců se použije, funguje-li je místní cíl jako adresát zprávy. Totéž platí pro mapy odesílatelů.<br/>\n Místní cíl se nedozví, selže-li doručení na cíl BCC.",
|
||||||
"bcc_local_dest": "Týká se",
|
"bcc_local_dest": "Týká se",
|
||||||
"bcc_map": "Skrytá kopie",
|
"bcc_map": "Skrytá kopie",
|
||||||
"bcc_map_type": "Typ skryté kopie",
|
"bcc_map_type": "Typ skryté kopie",
|
||||||
@@ -840,7 +938,7 @@
|
|||||||
"last_run_reset": "Znovu naplánovat",
|
"last_run_reset": "Znovu naplánovat",
|
||||||
"mailbox": "Mailová schránka",
|
"mailbox": "Mailová schránka",
|
||||||
"mailbox_defaults": "Výchozí nastavení",
|
"mailbox_defaults": "Výchozí nastavení",
|
||||||
"mailbox_defaults_info": "Definuje výchozí nastavení pro nové schránky",
|
"mailbox_defaults_info": "Definuje výchozí nastavení pro nové schránky.",
|
||||||
"mailbox_defquota": "Výchozí velikost schránky",
|
"mailbox_defquota": "Výchozí velikost schránky",
|
||||||
"mailbox_templates": "Šablony schránek",
|
"mailbox_templates": "Šablony schránek",
|
||||||
"mailbox_quota": "Max. velikost schránky",
|
"mailbox_quota": "Max. velikost schránky",
|
||||||
@@ -860,7 +958,7 @@
|
|||||||
"private_comment": "Soukromý komentář",
|
"private_comment": "Soukromý komentář",
|
||||||
"public_comment": "Veřejný komentář",
|
"public_comment": "Veřejný komentář",
|
||||||
"q_add_header": "Složka nevyžádaná pošta",
|
"q_add_header": "Složka nevyžádaná pošta",
|
||||||
"q_all": "Všechny kategorie",
|
"q_all": " Nevyžádaná pošta a Odmítnuta",
|
||||||
"q_reject": "Odmítnuta",
|
"q_reject": "Odmítnuta",
|
||||||
"quarantine_category": "Kategorie oznámení karantény",
|
"quarantine_category": "Kategorie oznámení karantény",
|
||||||
"quarantine_notification": "Upozornění z karantény",
|
"quarantine_notification": "Upozornění z karantény",
|
||||||
@@ -869,7 +967,7 @@
|
|||||||
"recipient_map": "Mapa příjemce",
|
"recipient_map": "Mapa příjemce",
|
||||||
"recipient_map_info": "Mapy příjemců slouží k nahrazení cílové adresy zprávy před doručením.",
|
"recipient_map_info": "Mapy příjemců slouží k nahrazení cílové adresy zprávy před doručením.",
|
||||||
"recipient_map_new": "Nový přijemce",
|
"recipient_map_new": "Nový přijemce",
|
||||||
"recipient_map_new_info": "Cílová adresa mapy příjemce musí být emailová adresa nebo název domény.",
|
"recipient_map_new_info": "Cílovou adresou mapy příjemců musí být emailová adresa nebo název domény.",
|
||||||
"recipient_map_old": "Původní příjemce",
|
"recipient_map_old": "Původní příjemce",
|
||||||
"recipient_map_old_info": "Původní příjemce musí být platná emailová adresa nebo název domény.",
|
"recipient_map_old_info": "Původní příjemce musí být platná emailová adresa nebo název domény.",
|
||||||
"recipient_maps": "Mapy příjemců",
|
"recipient_maps": "Mapy příjemců",
|
||||||
@@ -888,7 +986,7 @@
|
|||||||
"sieve_preset_5": "Automatický odpovídač (dovolená)",
|
"sieve_preset_5": "Automatický odpovídač (dovolená)",
|
||||||
"sieve_preset_6": "Odmítnout zprávu s odpovědí",
|
"sieve_preset_6": "Odmítnout zprávu s odpovědí",
|
||||||
"sieve_preset_7": "Přesměrovat a ponechat/zahodit",
|
"sieve_preset_7": "Přesměrovat a ponechat/zahodit",
|
||||||
"sieve_preset_8": "Zahodit zprávu poslanou na alias, do něhož patří i odesílatel",
|
"sieve_preset_8": "Zprávu od určitého odesílatele přesměrovat, označit jako přečtenou a uložit do složky",
|
||||||
"sieve_preset_header": "Vizte následující ukázková pravidla. Více informací na <a href=\"https://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)\" target=\"_blank\">Wikipedii</a>.",
|
"sieve_preset_header": "Vizte následující ukázková pravidla. Více informací na <a href=\"https://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)\" target=\"_blank\">Wikipedii</a>.",
|
||||||
"sogo_visible": "Alias dostupný v SOGo",
|
"sogo_visible": "Alias dostupný v SOGo",
|
||||||
"sogo_visible_n": "Skrýt alias v SOGo",
|
"sogo_visible_n": "Skrýt alias v SOGo",
|
||||||
@@ -928,7 +1026,9 @@
|
|||||||
"waiting": "Čekání",
|
"waiting": "Čekání",
|
||||||
"weekly": "Každý týden",
|
"weekly": "Každý týden",
|
||||||
"yes": "✓",
|
"yes": "✓",
|
||||||
"relay_unknown": "Předávání neexistujících schránek"
|
"relay_unknown": "Předávání neexistujících schránek",
|
||||||
|
"iam": "Poskytovatel identity",
|
||||||
|
"internal": "Interní"
|
||||||
},
|
},
|
||||||
"oauth2": {
|
"oauth2": {
|
||||||
"access_denied": "K udělení přístupu se přihlašte jako vlastník mailové schránky.",
|
"access_denied": "K udělení přístupu se přihlašte jako vlastník mailové schránky.",
|
||||||
@@ -936,7 +1036,7 @@
|
|||||||
"deny": "Zamítnout",
|
"deny": "Zamítnout",
|
||||||
"permit": "Ověřit aplikaci",
|
"permit": "Ověřit aplikaci",
|
||||||
"profile": "Profil",
|
"profile": "Profil",
|
||||||
"profile_desc": "Zobrazit osobní údaje: uživ. jméno, jméno, datum vytvoření a úpravy, stav",
|
"profile_desc": "Zobrazit osobní údaje: uživ. jméno, celé jméno, datum vytvoření a úpravy, stav",
|
||||||
"scope_ask_permission": "Aplikace požádala o následující oprávnění"
|
"scope_ask_permission": "Aplikace požádala o následující oprávnění"
|
||||||
},
|
},
|
||||||
"quarantine": {
|
"quarantine": {
|
||||||
@@ -960,7 +1060,7 @@
|
|||||||
"notified": "Oznámeno",
|
"notified": "Oznámeno",
|
||||||
"qhandler_success": "Požadavek úspěšně přijat. Můžete nyní zavřít okno.",
|
"qhandler_success": "Požadavek úspěšně přijat. Můžete nyní zavřít okno.",
|
||||||
"qid": "Rspamd QID",
|
"qid": "Rspamd QID",
|
||||||
"qinfo": "Karanténní systém uloží odmítnutou poštu do databáze (odesílatel se <em>nedozví</em>, že pošta byla doručena) jakož i pošta, která bude jako kopie doručena do složky Nevyžádaná pošta. \r\n<br>\"Naučit jako spam a smazat\" naučí zprávu jako spam přes Bayesian theorem a současně vypočítá fuzzy hashes pro odmítnutí podobných zpráv v budoucnosti. \r\n<br> Prosím, berte na vědomí, že naučení více zpráv může být - záleží na vašem systému - časově náročné . <br> Položky na černé listině jsou z karantény vyloučeny.",
|
"qinfo": "Karanténa uloží do databáze odmítnutou poštu (odesílatel se <em>nedozví</em>, že pošta byla doručena) jakož i poštu, jež se jako kopie doručuje do složky Nevyžádaná pošta.\n <br>\"Naučit jako spam a smazat\" předá zprávu systému k naučení bayesiánskou analýzou jako spam a současně stanoví fuzzy hashe pro odmítání podobných zpráv v budoucnosti.\n <br> Vezměte na vědomí, že učení více zpráv může být podle výkonnosti systému zabrat více času. <br> Položky na seznamu zákazů jsou z karantény vyloučeny.",
|
||||||
"qitem": "Položka v karanténě",
|
"qitem": "Položka v karanténě",
|
||||||
"quarantine": "Karanténa",
|
"quarantine": "Karanténa",
|
||||||
"quick_actions": "Akce",
|
"quick_actions": "Akce",
|
||||||
@@ -1005,7 +1105,8 @@
|
|||||||
"hold_mail_legend": "Podrží vybrané e-maily. (Zabrání dalším pokusům o doručení)",
|
"hold_mail_legend": "Podrží vybrané e-maily. (Zabrání dalším pokusům o doručení)",
|
||||||
"show_message": "Zobrazit zprávu",
|
"show_message": "Zobrazit zprávu",
|
||||||
"unhold_mail": "Uvolnit",
|
"unhold_mail": "Uvolnit",
|
||||||
"unhold_mail_legend": "Uvolnit vybrané e-maily k doručení. (Pouze v případě předchozího podržení)"
|
"unhold_mail_legend": "Uvolnit vybrané e-maily k doručení. (Pouze v případě předchozího podržení)",
|
||||||
|
"unban": "odblokovat"
|
||||||
},
|
},
|
||||||
"ratelimit": {
|
"ratelimit": {
|
||||||
"disabled": "Vypnuto",
|
"disabled": "Vypnuto",
|
||||||
@@ -1066,7 +1167,7 @@
|
|||||||
"logged_in_as": "Přihlášen jako %s",
|
"logged_in_as": "Přihlášen jako %s",
|
||||||
"mailbox_added": "Mailová schránka %s přidána",
|
"mailbox_added": "Mailová schránka %s přidána",
|
||||||
"mailbox_modified": "Změny mailové schránky %s uloženy",
|
"mailbox_modified": "Změny mailové schránky %s uloženy",
|
||||||
"mailbox_removed": "Mailová schránka %s odebrána",
|
"mailbox_removed": "Mailová schránka %s odstraněna",
|
||||||
"nginx_reloaded": "Nginx reload byl úspěšný",
|
"nginx_reloaded": "Nginx reload byl úspěšný",
|
||||||
"object_modified": "Změny objektu %s uloženy",
|
"object_modified": "Změny objektu %s uloženy",
|
||||||
"password_policy_saved": "Politika hesel byla úspěšně uložena",
|
"password_policy_saved": "Politika hesel byla úspěšně uložena",
|
||||||
@@ -1100,7 +1201,14 @@
|
|||||||
"verified_yotp_login": "Yubico OTP přihlášení ověřeno",
|
"verified_yotp_login": "Yubico OTP přihlášení ověřeno",
|
||||||
"cors_headers_edited": "Nastavení CORS byla uložena",
|
"cors_headers_edited": "Nastavení CORS byla uložena",
|
||||||
"domain_footer_modified": "Změny patičky domény %s byly uloženy",
|
"domain_footer_modified": "Změny patičky domény %s byly uloženy",
|
||||||
"recovery_email_sent": "E-mail k obnovení byl odeslán na adresu %s"
|
"recovery_email_sent": "E-mail k obnovení byl odeslán na adresu %s",
|
||||||
|
"custom_login_modified": "Úpravy přihlašování úspěšně uloženy",
|
||||||
|
"domain_add_dkim_available": "Klíč DKIM už existoval",
|
||||||
|
"f2b_banlist_refreshed": "Seznam zákazů úspěšně obnoven.",
|
||||||
|
"iam_test_connection": "Spojení úspěšně navázano",
|
||||||
|
"ip_check_opt_in_modified": "Kontrola IP adresy úspěšně uložena",
|
||||||
|
"mailbox_renamed": "Schránka přejmenována z %s na %s",
|
||||||
|
"password_changed_success": "Heslo úspěšně změněno"
|
||||||
},
|
},
|
||||||
"tfa": {
|
"tfa": {
|
||||||
"api_register": "%s používá Yubico Cloud API. Prosím získejte API klíč pro své Yubico <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">ZDE</a>",
|
"api_register": "%s používá Yubico Cloud API. Prosím získejte API klíč pro své Yubico <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">ZDE</a>",
|
||||||
@@ -1116,7 +1224,7 @@
|
|||||||
"none": "Deaktivovat",
|
"none": "Deaktivovat",
|
||||||
"reload_retry": "- (znovu načtěte stránku, opakuje-li se chyba)",
|
"reload_retry": "- (znovu načtěte stránku, opakuje-li se chyba)",
|
||||||
"scan_qr_code": "Prosím načtěte následující kód svou aplikací na ověření nebo zadejte kód ručně.",
|
"scan_qr_code": "Prosím načtěte následující kód svou aplikací na ověření nebo zadejte kód ručně.",
|
||||||
"select": "Prosím vyberte...",
|
"select": "Vyberte prosím",
|
||||||
"set_tfa": "Nastavení způsobu dvoufaktorového ověření",
|
"set_tfa": "Nastavení způsobu dvoufaktorového ověření",
|
||||||
"start_webauthn_validation": "Zahájit inicializaci",
|
"start_webauthn_validation": "Zahájit inicializaci",
|
||||||
"tfa": "Dvoufaktorové ověření (TFA)",
|
"tfa": "Dvoufaktorové ověření (TFA)",
|
||||||
@@ -1125,7 +1233,10 @@
|
|||||||
"webauthn": "WebAuthn ověření",
|
"webauthn": "WebAuthn ověření",
|
||||||
"waiting_usb_auth": "<i>Čeká se na USB zařízení...</i><br><br>Prosím stiskněte tlačítko na svém WebAuthn USB zařízení.",
|
"waiting_usb_auth": "<i>Čeká se na USB zařízení...</i><br><br>Prosím stiskněte tlačítko na svém WebAuthn USB zařízení.",
|
||||||
"waiting_usb_register": "<i>Čeká se na USB zařízení...</i><br><br>Prosím zadejte své heslo výše a potvrďte WebAuthn registraci stiskem tlačítka na svém WebAuthn USB zařízení.",
|
"waiting_usb_register": "<i>Čeká se na USB zařízení...</i><br><br>Prosím zadejte své heslo výše a potvrďte WebAuthn registraci stiskem tlačítka na svém WebAuthn USB zařízení.",
|
||||||
"yubi_otp": "Yubico OTP ověření"
|
"yubi_otp": "Yubico OTP ověření",
|
||||||
|
"u2f_deprecated": "Zdá se, že váš klíč byl registrován zastaralou metodou U2F. Dojde k deaktivaci dvoufaktorové autentifikace a smazání klíče.",
|
||||||
|
"authenticators": "Autentifikátory",
|
||||||
|
"u2f_deprecated_important": "Registrujte svůj klíč novou metodou WebAuthn ve správě správců."
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"action": "Akce",
|
"action": "Akce",
|
||||||
@@ -1141,7 +1252,7 @@
|
|||||||
"alias_time_left": "Zbývající čas",
|
"alias_time_left": "Zbývající čas",
|
||||||
"alias_valid_until": "Platný do",
|
"alias_valid_until": "Platný do",
|
||||||
"aliases_also_send_as": "Smí odesílat také jako uživatel",
|
"aliases_also_send_as": "Smí odesílat také jako uživatel",
|
||||||
"aliases_send_as_all": "Nekontrolovat přístup odesílatele pro následující doménu(y) a jejich aliasy domény:",
|
"aliases_send_as_all": "Nekontrolovat přístup odesílatele pro následující doménu(y) a jejich aliasy",
|
||||||
"allowed_protocols": "Povolené protokoly",
|
"allowed_protocols": "Povolené protokoly",
|
||||||
"app_hint": "Hesla aplikací jsou alternativní heslo pro přihlášení k IMAP, SMTP, CalDAV, CardDAV a EAS. Uživatelské jméno zůstává stejné.<br>SOGo však nelze s heslem aplikace použít.",
|
"app_hint": "Hesla aplikací jsou alternativní heslo pro přihlášení k IMAP, SMTP, CalDAV, CardDAV a EAS. Uživatelské jméno zůstává stejné.<br>SOGo však nelze s heslem aplikace použít.",
|
||||||
"app_name": "Název aplikace",
|
"app_name": "Název aplikace",
|
||||||
@@ -1162,8 +1273,8 @@
|
|||||||
"description": "Popis",
|
"description": "Popis",
|
||||||
"delete_ays": "Potvrďte odstranění.",
|
"delete_ays": "Potvrďte odstranění.",
|
||||||
"direct_aliases": "Přímé aliasy",
|
"direct_aliases": "Přímé aliasy",
|
||||||
"direct_aliases_desc": "Na přímé aliasy se uplatňuje filtr spamu a nastavení pravidel TLS",
|
"direct_aliases_desc": "Na přímé aliasy se uplatňuje filtr spamu a nastavení pravidel TLS.",
|
||||||
"direct_protocol_access": "Tento uživatel mailové schránky má <b>přímý externí přístup</b> k následujícím protokolům a aplikacím. Toto nastavení je řízeno správcem. Pro udělení přístupu k jednotlivým protokolům a aplikacím lze vytvořit hesla aplikací.<br>Tlačítko \"Webmailu\" zajišťuje jednotné přihlášení k SOGo a je vždy k dispozici.",
|
"direct_protocol_access": "Tento uživatel mailové schránky má <b>přímý externí přístup</b> k následujícím protokolům a aplikacím. Toto nastavení je řízeno správcem. Pro udělení přístupu k jednotlivým protokolům a aplikacím lze vytvořit hesla aplikací.<br>Tlačítko \"Webmail\" zajišťuje jednotné přihlášení k SOGo a je vždy k dispozici.",
|
||||||
"eas_reset": "Smazat mezipaměť zařízení ActiveSync",
|
"eas_reset": "Smazat mezipaměť zařízení ActiveSync",
|
||||||
"eas_reset_help": "Obnovení mezipaměti zařízení pomůže zpravidla obnovit poškozený profil služby ActiveSync.<br><b>Upozornění:</b> Všechna data budou opětovně stažena!",
|
"eas_reset_help": "Obnovení mezipaměti zařízení pomůže zpravidla obnovit poškozený profil služby ActiveSync.<br><b>Upozornění:</b> Všechna data budou opětovně stažena!",
|
||||||
"eas_reset_now": "Smazat",
|
"eas_reset_now": "Smazat",
|
||||||
@@ -1204,7 +1315,7 @@
|
|||||||
"no_last_login": "Žádný záznam o přihlášení",
|
"no_last_login": "Žádný záznam o přihlášení",
|
||||||
"no_record": "Žádný záznam",
|
"no_record": "Žádný záznam",
|
||||||
"open_logs": "Otevřít záznam",
|
"open_logs": "Otevřít záznam",
|
||||||
"open_webmail_sso": "Webmailu",
|
"open_webmail_sso": "Webmail",
|
||||||
"password": "Heslo",
|
"password": "Heslo",
|
||||||
"password_now": "Současné heslo (pro potvrzení změny)",
|
"password_now": "Současné heslo (pro potvrzení změny)",
|
||||||
"password_repeat": "Heslo (znovu)",
|
"password_repeat": "Heslo (znovu)",
|
||||||
@@ -1236,13 +1347,13 @@
|
|||||||
"sogo_profile_reset": "Resetovat profil SOGo",
|
"sogo_profile_reset": "Resetovat profil SOGo",
|
||||||
"sogo_profile_reset_help": "Tato volba odstraní uživatelský profil SOGo a <b>nenávratně vymaže všechna data</b>.",
|
"sogo_profile_reset_help": "Tato volba odstraní uživatelský profil SOGo a <b>nenávratně vymaže všechna data</b>.",
|
||||||
"sogo_profile_reset_now": "Resetovat profil",
|
"sogo_profile_reset_now": "Resetovat profil",
|
||||||
"spam_aliases": "Dočasné e-mailové aliasy",
|
"spam_aliases": "Spam aliasy",
|
||||||
"spam_score_reset": "Obnovit výchozí nastavení serveru",
|
"spam_score_reset": "Obnovit výchozí nastavení serveru",
|
||||||
"spamfilter": "Filtr spamu",
|
"spamfilter": "Filtr spamu",
|
||||||
"spamfilter_behavior": "Hodnocení",
|
"spamfilter_behavior": "Hodnocení",
|
||||||
"spamfilter_bl": "Seznam zakázaných adres (blacklist)",
|
"spamfilter_bl": "Seznam zákazů",
|
||||||
"spamfilter_bl_desc": "Zakázané emailové adresy <b>budou vždy klasifikovány jako spam a odmítnuty</b>. Lze použít zástupné znaky (*). Filtr se použije pouze na přímé aliasy (s jednou cílovou mailovou schránkou), s výjimkou doménových košů a samotné mailové schránky.",
|
"spamfilter_bl_desc": "Zakázané emailové adresy budou <b>vždy</b> klasifikovány jako spam a odmítnuty. Odmítnutá pošta <b>se neukládá</b> do karantény. Lze použít zástupné znaky (*). Filtr se použije pouze na přímé aliasy (s jednou cílovou poštovní schránkou), s výjimkou doménových košů a samotné poštovní schránky.",
|
||||||
"spamfilter_default_score": "Výchozí hodnoty:",
|
"spamfilter_default_score": "Výchozí hodnoty",
|
||||||
"spamfilter_green": "Zelená: tato zpráva není spam",
|
"spamfilter_green": "Zelená: tato zpráva není spam",
|
||||||
"spamfilter_hint": "První hodnota představuje \"nízké spam skóre\" a druhá \"vysoké spam skóre\".",
|
"spamfilter_hint": "První hodnota představuje \"nízké spam skóre\" a druhá \"vysoké spam skóre\".",
|
||||||
"spamfilter_red": "Červená: Tato zpráva je spam a server ji odmítne",
|
"spamfilter_red": "Červená: Tato zpráva je spam a server ji odmítne",
|
||||||
@@ -1252,7 +1363,7 @@
|
|||||||
"spamfilter_table_empty": "Žádná data k zobrazení",
|
"spamfilter_table_empty": "Žádná data k zobrazení",
|
||||||
"spamfilter_table_remove": "smazat",
|
"spamfilter_table_remove": "smazat",
|
||||||
"spamfilter_table_rule": "Pravidlo",
|
"spamfilter_table_rule": "Pravidlo",
|
||||||
"spamfilter_wl": "Seznam povolených adres (whitelist)",
|
"spamfilter_wl": "Seznam povolení",
|
||||||
"spamfilter_wl_desc": "Povolené emailové adresy <b>nebudou nikdy klasifikovány jako spam</b>. Lze použít zástupné znaky (*). Filtr se použije pouze na přímé aliasy (s jednou cílovou mailovou schránkou), s výjimkou doménových košů a samotné mailové schránky.",
|
"spamfilter_wl_desc": "Povolené emailové adresy <b>nebudou nikdy klasifikovány jako spam</b>. Lze použít zástupné znaky (*). Filtr se použije pouze na přímé aliasy (s jednou cílovou mailovou schránkou), s výjimkou doménových košů a samotné mailové schránky.",
|
||||||
"spamfilter_yellow": "Žlutá: tato zpráva může být spam, bude označena jako spam a přesunuta do složky nevyžádané pošty",
|
"spamfilter_yellow": "Žlutá: tato zpráva může být spam, bude označena jako spam a přesunuta do složky nevyžádané pošty",
|
||||||
"status": "Stav",
|
"status": "Stav",
|
||||||
@@ -1274,7 +1385,7 @@
|
|||||||
"tag_in_subject": "V předmětu",
|
"tag_in_subject": "V předmětu",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"title": "Předmět",
|
"title": "Předmět",
|
||||||
"tls_enforce_in": "Vynutit TLS pro příchozí poštu ",
|
"tls_enforce_in": "Vynutit TLS pro příchozí poštu",
|
||||||
"tls_enforce_out": "Vynutit TLS pro odchozí poštu",
|
"tls_enforce_out": "Vynutit TLS pro odchozí poštu",
|
||||||
"tls_policy": "Politika šifrování",
|
"tls_policy": "Politika šifrování",
|
||||||
"tls_policy_warning": "<strong>Varování:</strong> Pokud se rozhodnete vynutit šifrovaný přenos pošty, může dojít ke ztrátě e-mailů.<br>Zprávy, které nesplňují tuto politiku, budou mailovým systémem odmítnuty.<br>Tato volba ovlivňuje primární e-mailovou adresu (přihlašovací jméno), všechny adresy odvozené z doménových aliasů i aliasy, jež mají tuto mailovou schránku jako cíl.",
|
"tls_policy_warning": "<strong>Varování:</strong> Pokud se rozhodnete vynutit šifrovaný přenos pošty, může dojít ke ztrátě e-mailů.<br>Zprávy, které nesplňují tuto politiku, budou mailovým systémem odmítnuty.<br>Tato volba ovlivňuje primární e-mailovou adresu (přihlašovací jméno), všechny adresy odvozené z doménových aliasů i aliasy, jež mají tuto mailovou schránku jako cíl.",
|
||||||
@@ -1290,7 +1401,16 @@
|
|||||||
"years": "let",
|
"years": "let",
|
||||||
"pushover_sound": "Zvukové upozornění",
|
"pushover_sound": "Zvukové upozornění",
|
||||||
"password_reset_info": "Pokud není zadán e-mail pro obnovení hesla, nelze tuto funkci použít.",
|
"password_reset_info": "Pokud není zadán e-mail pro obnovení hesla, nelze tuto funkci použít.",
|
||||||
"pw_recovery_email": "E-mail pro obnovení hesla"
|
"pw_recovery_email": "E-mail pro obnovení hesla",
|
||||||
|
"tfa_info": "Dvoufaktorová autentizace vám pomáhá chránit svůj účet. Je-li zapnuta, musíte si vytvořit aplikační hesla pro aplikace, jež dvoufaktorovou autentizaci nepodporují (např. poštovní klienti).",
|
||||||
|
"attribute": "Atribut",
|
||||||
|
"authentication": "Autentifikace",
|
||||||
|
"overview": "Přehled",
|
||||||
|
"protocols": "Protokoly",
|
||||||
|
"value": "Hodnota",
|
||||||
|
"expire_never": "Nikdy nevyprší",
|
||||||
|
"forever": "Navždy",
|
||||||
|
"spam_aliases_info": "Spam alias je dočasná adresa, již lze použít k ochraně skutečných adres. <br>Případně lze nastavit také dobu platnosti, po níž je alias automaticky deaktivován, čímž se řeší případy zneužitých či odcizených adres."
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"cannot_delete_self": "Nelze smazat právě přihlášeného uživatele",
|
"cannot_delete_self": "Nelze smazat právě přihlášeného uživatele",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"sogo_access": "Verwalten des SOGo-Zugriffsrechts erlauben",
|
"sogo_access": "Verwalten des SOGo-Zugriffsrechts erlauben",
|
||||||
"sogo_profile_reset": "SOGo-Profil zurücksetzen",
|
"sogo_profile_reset": "SOGo-Profil zurücksetzen",
|
||||||
"spam_alias": "Temporäre E-Mail-Aliasse",
|
"spam_alias": "Temporäre E-Mail-Aliasse",
|
||||||
"spam_policy": "Blacklist/Whitelist",
|
"spam_policy": "Deny/Allowlist",
|
||||||
"spam_score": "Spam-Bewertung",
|
"spam_score": "Spam-Bewertung",
|
||||||
"syncjobs": "Sync Jobs",
|
"syncjobs": "Sync Jobs",
|
||||||
"tls_policy": "Verschlüsselungsrichtlinie",
|
"tls_policy": "Verschlüsselungsrichtlinie",
|
||||||
@@ -71,6 +71,8 @@
|
|||||||
"goto_spam": "Nachrichten als <span class=\"text-danger\"><b>Spam</b></span> lernen",
|
"goto_spam": "Nachrichten als <span class=\"text-danger\"><b>Spam</b></span> lernen",
|
||||||
"hostname": "Host",
|
"hostname": "Host",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
|
"internal": "Intern",
|
||||||
|
"internal_info": "Interne Aliasse sind nur von der eigenen Domäne oder Alias-Domänen erreichbar.",
|
||||||
"kind": "Art",
|
"kind": "Art",
|
||||||
"mailbox_quota_def": "Standard-Quota einer Mailbox",
|
"mailbox_quota_def": "Standard-Quota einer Mailbox",
|
||||||
"mailbox_quota_m": "Max. Speicherplatz pro Mailbox (MiB)",
|
"mailbox_quota_m": "Max. Speicherplatz pro Mailbox (MiB)",
|
||||||
@@ -134,6 +136,7 @@
|
|||||||
"admin_domains": "Domain-Zuweisungen",
|
"admin_domains": "Domain-Zuweisungen",
|
||||||
"admins": "Administratoren",
|
"admins": "Administratoren",
|
||||||
"admins_ldap": "LDAP-Administratoren",
|
"admins_ldap": "LDAP-Administratoren",
|
||||||
|
"admin_quicklink": "Quicklink zur Admin-Loginseite ausblenden",
|
||||||
"advanced_settings": "Erweiterte Einstellungen",
|
"advanced_settings": "Erweiterte Einstellungen",
|
||||||
"api_allow_from": "IP-Adressen oder Netzwerke (CIDR Notation) für Zugriff auf API",
|
"api_allow_from": "IP-Adressen oder Netzwerke (CIDR Notation) für Zugriff auf API",
|
||||||
"api_info": "Die API befindet sich noch in Entwicklung, die Dokumentation kann unter <a href=\"/api\">/api</a> abgerufen werden.",
|
"api_info": "Die API befindet sich noch in Entwicklung, die Dokumentation kann unter <a href=\"/api\">/api</a> abgerufen werden.",
|
||||||
@@ -146,7 +149,7 @@
|
|||||||
"arrival_time": "Ankunftszeit (Serverzeit)",
|
"arrival_time": "Ankunftszeit (Serverzeit)",
|
||||||
"authed_user": "Auth. Benutzer",
|
"authed_user": "Auth. Benutzer",
|
||||||
"ays": "Soll der Vorgang wirklich ausgeführt werden?",
|
"ays": "Soll der Vorgang wirklich ausgeführt werden?",
|
||||||
"ban_list_info": "Übersicht ausgesperrter Netzwerke: <b>Netzwerk (verbleibende Bannzeit) - [Aktionen]</b>.<br />IPs, die zum Entsperren eingereiht werden, verlassen die Liste aktiver Banns nach wenigen Sekunden.<br />Rote Labels sind Indikatoren für aktive Blacklist-Einträge.",
|
"ban_list_info": "Übersicht ausgesperrter Netzwerke: <b>Netzwerk (verbleibende Bannzeit) - [Aktionen]</b>.<br />IPs, die zum Entsperren eingereiht werden, verlassen die Liste aktiver Banns nach wenigen Sekunden.<br />Rote Labels sind Indikatoren für aktive Allowlist-Einträge.",
|
||||||
"change_logo": "Logo ändern",
|
"change_logo": "Logo ändern",
|
||||||
"configuration": "Konfiguration",
|
"configuration": "Konfiguration",
|
||||||
"convert_html_to_text": "Konvertiere HTML zu reinem Text",
|
"convert_html_to_text": "Konvertiere HTML zu reinem Text",
|
||||||
@@ -155,6 +158,7 @@
|
|||||||
"credentials_transport_warning": "<b>Warnung</b>: Das Hinzufügen einer neuen Regel bewirkt die Aktualisierung der Authentifizierungsdaten aller vorhandenen Einträge mit identischem Next Hop.",
|
"credentials_transport_warning": "<b>Warnung</b>: Das Hinzufügen einer neuen Regel bewirkt die Aktualisierung der Authentifizierungsdaten aller vorhandenen Einträge mit identischem Next Hop.",
|
||||||
"customer_id": "Kunde",
|
"customer_id": "Kunde",
|
||||||
"customize": "UI-Anpassung",
|
"customize": "UI-Anpassung",
|
||||||
|
"login_page": "Login-Seite",
|
||||||
"destination": "Ziel",
|
"destination": "Ziel",
|
||||||
"dkim_add_key": "ARC/DKIM-Key hinzufügen",
|
"dkim_add_key": "ARC/DKIM-Key hinzufügen",
|
||||||
"dkim_domains_selector": "Selector",
|
"dkim_domains_selector": "Selector",
|
||||||
@@ -173,6 +177,7 @@
|
|||||||
"domain": "Domain",
|
"domain": "Domain",
|
||||||
"domain_admin": "Administrator hinzufügen",
|
"domain_admin": "Administrator hinzufügen",
|
||||||
"domain_admins": "Domain-Administratoren",
|
"domain_admins": "Domain-Administratoren",
|
||||||
|
"domainadmin_quicklink": "Quicklink zur Domainadmin-Loginseite ausblenden",
|
||||||
"domain_s": "Domain(s)",
|
"domain_s": "Domain(s)",
|
||||||
"duplicate": "Duplizieren",
|
"duplicate": "Duplizieren",
|
||||||
"duplicate_dkim": "DKIM duplizieren",
|
"duplicate_dkim": "DKIM duplizieren",
|
||||||
@@ -181,9 +186,9 @@
|
|||||||
"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_ban_time_increment": "Bannzeit erhöht sich mit jedem Bann",
|
||||||
"f2b_blacklist": "Blacklist für Netzwerke und Hosts",
|
"f2b_blacklist": "Denyliste 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 Denyliste wird immer eine Allowlist-Einheit überwiegen. <b>Die Aktualisierung der Liste dauert einige Sekunden.</b>",
|
||||||
"f2b_manage_external": "Fail2Ban extern verwalten",
|
"f2b_manage_external": "Fail2Ban extern verwalten",
|
||||||
"f2b_manage_external_info": "Fail2ban wird die Banlist weiterhin pflegen, jedoch werden keine aktiven Regeln zum blockieren gesetzt. Die unten generierte Banlist, kann verwendet werden, um den Datenverkehr extern zu blockieren.",
|
"f2b_manage_external_info": "Fail2ban wird die Banlist weiterhin pflegen, jedoch werden keine aktiven Regeln zum blockieren gesetzt. Die unten generierte Banlist, kann verwendet werden, um den Datenverkehr extern zu blockieren.",
|
||||||
"f2b_max_attempts": "Max. Versuche",
|
"f2b_max_attempts": "Max. Versuche",
|
||||||
@@ -193,8 +198,10 @@
|
|||||||
"f2b_parameters": "Fail2ban-Parameter",
|
"f2b_parameters": "Fail2ban-Parameter",
|
||||||
"f2b_regex_info": "Berücksichtigte Logs: SOGo, Postfix, Dovecot, PHP-FPM.",
|
"f2b_regex_info": "Berücksichtigte Logs: SOGo, Postfix, Dovecot, PHP-FPM.",
|
||||||
"f2b_retry_window": "Wiederholungen im Zeitraum von (s)",
|
"f2b_retry_window": "Wiederholungen im Zeitraum von (s)",
|
||||||
"f2b_whitelist": "Whitelist für Netzwerke und Hosts",
|
"f2b_whitelist": "Allowliste für Netzwerke und Hosts",
|
||||||
"filter_table": "Tabelle filtern",
|
"filter_table": "Tabelle filtern",
|
||||||
|
"force_sso_text": "Wenn ein externer OIDC-Provider konfiguriert ist, blendet diese Option die mailcow-Loginform aus und zeigt nur den Single-Sign-On-Button an.",
|
||||||
|
"force_sso": "mailcow-Login deaktivieren und nur Single Sign-On anzeigen",
|
||||||
"forwarding_hosts": "Weiterleitungs-Hosts",
|
"forwarding_hosts": "Weiterleitungs-Hosts",
|
||||||
"forwarding_hosts_add_hint": "Sie können entweder IPv4-/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.",
|
"forwarding_hosts_add_hint": "Sie können entweder IPv4-/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.",
|
||||||
"forwarding_hosts_hint": "Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt, optional kann er aber in den Spam-Ordner einsortiert werden. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem mailcow-Server eingerichtet wurde.",
|
"forwarding_hosts_hint": "Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt, optional kann er aber in den Spam-Ordner einsortiert werden. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem mailcow-Server eingerichtet wurde.",
|
||||||
@@ -222,6 +229,7 @@
|
|||||||
"iam_host": "Host",
|
"iam_host": "Host",
|
||||||
"iam_host_info": "Gib einen oder mehrere LDAP-Hosts ein, getrennt durch Kommas.",
|
"iam_host_info": "Gib einen oder mehrere LDAP-Hosts ein, getrennt durch Kommas.",
|
||||||
"iam_import_users": "Importiere Benutzer",
|
"iam_import_users": "Importiere Benutzer",
|
||||||
|
"iam_login_provisioning": "Benutzer beim Login erstellen",
|
||||||
"iam_mapping": "Attribut Mapping",
|
"iam_mapping": "Attribut Mapping",
|
||||||
"iam_bindpass": "Bind Passwort",
|
"iam_bindpass": "Bind Passwort",
|
||||||
"iam_periodic_full_sync": "Vollsynchronisation",
|
"iam_periodic_full_sync": "Vollsynchronisation",
|
||||||
@@ -238,7 +246,9 @@
|
|||||||
"iam_username_field": "Username Feld",
|
"iam_username_field": "Username Feld",
|
||||||
"iam_binddn": "Bind DN",
|
"iam_binddn": "Bind DN",
|
||||||
"iam_use_ssl": "Benutze SSL",
|
"iam_use_ssl": "Benutze SSL",
|
||||||
"iam_use_tls": "Benutze TLS",
|
"iam_use_ssl_info": "Wenn SSL aktiviert ist und der Port auf 389 gesetzt wurde, wird dieser automatisch auf 636 geändert.",
|
||||||
|
"iam_use_tls": "Benutze StartTLS",
|
||||||
|
"iam_use_tls_info": "Wenn TLS aktiviert wird, muss der Standardport deines LDAP-Servers (389) verwendet werden. SSL-Ports können dabei nicht verwendet werden.",
|
||||||
"iam_version": "Version",
|
"iam_version": "Version",
|
||||||
"ignore_ssl_error": "Ignoriere SSL Fehler",
|
"ignore_ssl_error": "Ignoriere SSL Fehler",
|
||||||
"import": "Importieren",
|
"import": "Importieren",
|
||||||
@@ -264,6 +274,7 @@
|
|||||||
"message": "Nachricht",
|
"message": "Nachricht",
|
||||||
"message_size": "Nachrichtengröße",
|
"message_size": "Nachrichtengröße",
|
||||||
"nexthop": "Next Hop",
|
"nexthop": "Next Hop",
|
||||||
|
"needs_restart": "benötigt Neustart",
|
||||||
"no": "✕",
|
"no": "✕",
|
||||||
"no_active_bans": "Keine aktiven Banns",
|
"no_active_bans": "Keine aktiven Banns",
|
||||||
"no_new_rows": "Keine weiteren Zeilen vorhanden",
|
"no_new_rows": "Keine weiteren Zeilen vorhanden",
|
||||||
@@ -306,6 +317,7 @@
|
|||||||
"quarantine_release_format_att": "Als Anhang",
|
"quarantine_release_format_att": "Als Anhang",
|
||||||
"quarantine_release_format_raw": "Unverändertes Original",
|
"quarantine_release_format_raw": "Unverändertes Original",
|
||||||
"quarantine_retention_size": "Rückhaltungen pro Mailbox:<br><small>0 bedeutet <b>inaktiv</b>.</small>",
|
"quarantine_retention_size": "Rückhaltungen pro Mailbox:<br><small>0 bedeutet <b>inaktiv</b>.</small>",
|
||||||
|
"quicklink_text": "Quicklinks zu anderen Login-Seiten unter der Loginform ein- oder ausblenden",
|
||||||
"quota_notification_html": "Benachrichtigungs-E-Mail Inhalt:<br><small>Leer lassen, um Standard-Template wiederherzustellen.</small>",
|
"quota_notification_html": "Benachrichtigungs-E-Mail Inhalt:<br><small>Leer lassen, um Standard-Template wiederherzustellen.</small>",
|
||||||
"quota_notification_sender": "Benachrichtigungs-E-Mail Absender",
|
"quota_notification_sender": "Benachrichtigungs-E-Mail Absender",
|
||||||
"quota_notification_subject": "Benachrichtigungs-E-Mail Betreff",
|
"quota_notification_subject": "Benachrichtigungs-E-Mail Betreff",
|
||||||
@@ -345,8 +357,8 @@
|
|||||||
"rspamd_com_settings": "Ein Name wird automatisch generiert. Beispielinhalte zur Einsicht stehen nachstehend bereit. Siehe auch <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd docs</a>",
|
"rspamd_com_settings": "Ein Name wird automatisch generiert. Beispielinhalte zur Einsicht stehen nachstehend bereit. Siehe auch <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd docs</a>",
|
||||||
"rspamd_global_filters": "Globale Filter-Maps",
|
"rspamd_global_filters": "Globale Filter-Maps",
|
||||||
"rspamd_global_filters_agree": "Ich werde vorsichtig sein!",
|
"rspamd_global_filters_agree": "Ich werde vorsichtig sein!",
|
||||||
"rspamd_global_filters_info": "Globale Filter-Maps steuern globales White- und Blacklisting dieses Servers.",
|
"rspamd_global_filters_info": "Globale Filter-Maps steuern globales Allow- und Denylisting dieses Servers.",
|
||||||
"rspamd_global_filters_regex": "Die akzeptierte Form für Einträge sind <b>ausschließlich</b> Regular Expressions.\r\n Trotz rudimentärer Überprüfung der Map, kann es zu fehlerhaften Einträgen kommen, die Rspamd im schlechtesten Fall mit unvorhersehbarer Funktionalität bestraft.<br>\r\n Das korrekte Format lautet \"/pattern/options\" (Beispiel: <code>/.+@domain\\.tld/i</code>).<br>\r\n Der Name der Map beschreibt die jeweilige Funktion.<br>\r\n Rspamd versucht die Maps umgehend aufzulösen. Bei Problemen sollte <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">Rspamd manuell neugestartet werden</a>.<br>Elemente auf Blacklists sind von der Quarantäne ausgeschlossen.",
|
"rspamd_global_filters_regex": "Die akzeptierte Form für Einträge sind <b>ausschließlich</b> Regular Expressions.\r\n Trotz rudimentärer Überprüfung der Map, kann es zu fehlerhaften Einträgen kommen, die Rspamd im schlechtesten Fall mit unvorhersehbarer Funktionalität bestraft.<br>\r\n Das korrekte Format lautet \"/pattern/options\" (Beispiel: <code>/.+@domain\\.tld/i</code>).<br>\r\n Der Name der Map beschreibt die jeweilige Funktion.<br>\r\n Rspamd versucht die Maps umgehend aufzulösen. Bei Problemen sollte <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">Rspamd manuell neugestartet werden</a>.<br>Elemente auf Denylisten sind von der Quarantäne ausgeschlossen.",
|
||||||
"rspamd_settings_map": "Rspamd-Settings-Map",
|
"rspamd_settings_map": "Rspamd-Settings-Map",
|
||||||
"sal_level": "Moo-Level",
|
"sal_level": "Moo-Level",
|
||||||
"save": "Änderungen speichern",
|
"save": "Änderungen speichern",
|
||||||
@@ -385,6 +397,7 @@
|
|||||||
"unchanged_if_empty": "Unverändert, wenn leer",
|
"unchanged_if_empty": "Unverändert, wenn leer",
|
||||||
"upload": "Hochladen",
|
"upload": "Hochladen",
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
|
"user_quicklink": "Quicklink zur Benutzer-Loginseite ausblenden",
|
||||||
"validate_license_now": "GUID erneut verifizieren",
|
"validate_license_now": "GUID erneut verifizieren",
|
||||||
"verify": "Verifizieren",
|
"verify": "Verifizieren",
|
||||||
"yes": "✓",
|
"yes": "✓",
|
||||||
@@ -396,7 +409,9 @@
|
|||||||
"allowed_methods": "Access-Control-Allow-Methods",
|
"allowed_methods": "Access-Control-Allow-Methods",
|
||||||
"allowed_origins": "Access-Control-Allow-Origin",
|
"allowed_origins": "Access-Control-Allow-Origin",
|
||||||
"logo_dark_label": "Invertiert für den Darkmode",
|
"logo_dark_label": "Invertiert für den Darkmode",
|
||||||
"logo_normal_label": "Normal"
|
"logo_normal_label": "Normal",
|
||||||
|
"user_link": "Nutzer-Link",
|
||||||
|
"filter": "Filter"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
|
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
|
||||||
@@ -470,10 +485,13 @@
|
|||||||
"mailboxes_in_use": "Maximale Anzahl an Mailboxen muss größer oder gleich %d sein",
|
"mailboxes_in_use": "Maximale Anzahl an Mailboxen muss größer oder gleich %d sein",
|
||||||
"malformed_username": "Benutzername hat ein falsches Format",
|
"malformed_username": "Benutzername hat ein falsches Format",
|
||||||
"map_content_empty": "Inhalt darf nicht leer sein",
|
"map_content_empty": "Inhalt darf nicht leer sein",
|
||||||
|
"max_age_invalid": "Maximales Alter %s ist ungültig",
|
||||||
"max_alias_exceeded": "Anzahl an Alias-Adressen überschritten",
|
"max_alias_exceeded": "Anzahl an Alias-Adressen überschritten",
|
||||||
"max_mailbox_exceeded": "Anzahl an Mailboxen überschritten (%d von %d)",
|
"max_mailbox_exceeded": "Anzahl an Mailboxen überschritten (%d von %d)",
|
||||||
"max_quota_in_use": "Mailbox-Speicherplatzlimit muss größer oder gleich %d MiB sein",
|
"max_quota_in_use": "Mailbox-Speicherplatzlimit muss größer oder gleich %d MiB sein",
|
||||||
"maxquota_empty": "Max. Speicherplatz pro Mailbox darf nicht 0 sein.",
|
"maxquota_empty": "Max. Speicherplatz pro Mailbox darf nicht 0 sein.",
|
||||||
|
"mode_invalid": "Modus %s ist ungültig",
|
||||||
|
"mx_invalid": "MX-Eintrag %s ist ungültig",
|
||||||
"mysql_error": "MySQL-Fehler: %s",
|
"mysql_error": "MySQL-Fehler: %s",
|
||||||
"network_host_invalid": "Netzwerk oder Host ungültig: %s",
|
"network_host_invalid": "Netzwerk oder Host ungültig: %s",
|
||||||
"next_hop_interferes": "%s verhindert das Hinzufügen von Next Hop %s",
|
"next_hop_interferes": "%s verhindert das Hinzufügen von Next Hop %s",
|
||||||
@@ -533,10 +551,12 @@
|
|||||||
"username_invalid": "Benutzername %s kann nicht verwendet werden",
|
"username_invalid": "Benutzername %s kann nicht verwendet werden",
|
||||||
"validity_missing": "Bitte geben Sie eine Gültigkeitsdauer an",
|
"validity_missing": "Bitte geben Sie eine Gültigkeitsdauer an",
|
||||||
"value_missing": "Bitte alle Felder ausfüllen",
|
"value_missing": "Bitte alle Felder ausfüllen",
|
||||||
|
"version_invalid": "Version %s ist ungültig",
|
||||||
"yotp_verification_failed": "Yubico OTP-Verifizierung fehlgeschlagen: %s",
|
"yotp_verification_failed": "Yubico OTP-Verifizierung fehlgeschlagen: %s",
|
||||||
"template_exists": "Vorlage %s existiert bereits",
|
"template_exists": "Vorlage %s existiert bereits",
|
||||||
"template_id_invalid": "Vorlagen-ID %s ungültig",
|
"template_id_invalid": "Vorlagen-ID %s ungültig",
|
||||||
"template_name_invalid": "Name der Vorlage ungültig"
|
"template_name_invalid": "Name der Vorlage ungültig",
|
||||||
|
"required_data_missing": "Die benötigten Daten: %s fehlen"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
"collapse_all": "Alle Einklappen",
|
"collapse_all": "Alle Einklappen",
|
||||||
@@ -672,6 +692,8 @@
|
|||||||
"grant_types": "Grant-types",
|
"grant_types": "Grant-types",
|
||||||
"hostname": "Servername",
|
"hostname": "Servername",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
|
"internal": "Intern",
|
||||||
|
"internal_info": "Interne Aliasse sind nur von der eigenen Domäne oder Alias-Domänen erreichbar.",
|
||||||
"kind": "Art",
|
"kind": "Art",
|
||||||
"last_modified": "Zuletzt geändert",
|
"last_modified": "Zuletzt geändert",
|
||||||
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*\\.google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
"lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*\\.google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
|
||||||
@@ -690,6 +712,17 @@
|
|||||||
"maxbytespersecond": "Max. Übertragungsrate in Bytes/s (0 für unlimitiert)",
|
"maxbytespersecond": "Max. Übertragungsrate in Bytes/s (0 für unlimitiert)",
|
||||||
"mbox_rl_info": "Dieses Limit wird auf den SASL Loginnamen angewendet und betrifft daher alle Absenderadressen, die der eingeloggte Benutzer verwendet. Bei Mailbox Ratelimit überwiegt ein Domain-weites Ratelimit.",
|
"mbox_rl_info": "Dieses Limit wird auf den SASL Loginnamen angewendet und betrifft daher alle Absenderadressen, die der eingeloggte Benutzer verwendet. Bei Mailbox Ratelimit überwiegt ein Domain-weites Ratelimit.",
|
||||||
"mins_interval": "Intervall (min)",
|
"mins_interval": "Intervall (min)",
|
||||||
|
"mta_sts": "MTA-STS",
|
||||||
|
"mta_sts_info": "<a href='https://de.wikipedia.org/wiki/STARTTLS#MTA-STS' target='_blank'>MTA-STS</a> ist ein Standard, der den E-Mail-Versand zwischen Mailservern zwingt, TLS mit gültigen Zertifikaten zu verwenden. <br>Er wird verwendet, wenn <a target='_blank' href='https://de.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities'>DANE</a> aufgrund fehlender oder nicht unterstützter DNSSEC nicht möglich ist.<br><b>Hinweis</b>: Wenn die empfangende Domain DANE mit DNSSEC unterstützt, wird DANE <b>immer</b> bevorzugt – MTA-STS fungiert nur als Fallback.",
|
||||||
|
"mta_sts_version": "Version",
|
||||||
|
"mta_sts_version_info": "Definiert die Version des MTA-STS-Standards – derzeit ist nur <code>STSv1</code> gültig.",
|
||||||
|
"mta_sts_mode": "Modus",
|
||||||
|
"mta_sts_mode_info": "Es gibt drei Modi zur Auswahl:<ul><li><em>testing</em> – Die Richtlinie wird nur überwacht, Verstöße haben keine Auswirkungen.</li><li><em>enforce</em> – Die Richtlinie wird strikt durchgesetzt, Verbindungen ohne gültiges TLS werden abgelehnt.</li><li><em>none</em> – Die Richtlinie wird veröffentlicht, aber nicht angewendet.</li></ul>",
|
||||||
|
"mta_sts_max_age": "Maximales Alter",
|
||||||
|
"mta_sts_max_age_info": "Zeit in Sekunden, die empfangende Mailserver diese Richtlinie zwischenspeichern dürfen, bevor sie erneut abgerufen wird.",
|
||||||
|
"mta_sts_mx": "MX-Server",
|
||||||
|
"mta_sts_mx_info": "Erlaubt das Senden nur an explizit aufgeführte Mailserver-Hostnamen; der sendende MTA überprüft, ob der DNS-MX-Hostname mit der Richtlinienliste übereinstimmt, und erlaubt die Zustellung nur mit einem gültigen TLS-Zertifikat (schützt vor MITM).",
|
||||||
|
"mta_sts_mx_notice": "Es können mehrere MX-Server angegeben werden (durch Kommas getrennt).",
|
||||||
"multiple_bookings": "Mehrfaches Buchen",
|
"multiple_bookings": "Mehrfaches Buchen",
|
||||||
"nexthop": "Next Hop",
|
"nexthop": "Next Hop",
|
||||||
"none_inherit": "Keine Auswahl / Erben",
|
"none_inherit": "Keine Auswahl / Erben",
|
||||||
@@ -736,7 +769,7 @@
|
|||||||
"sogo_visible_info": "Diese Option hat lediglich Einfluss auf Objekte, die in SOGo darstellbar sind (geteilte oder nicht-geteilte Alias-Adressen mit dem Ziel mindestens einer lokalen Mailbox).",
|
"sogo_visible_info": "Diese Option hat lediglich Einfluss auf Objekte, die in SOGo darstellbar sind (geteilte oder nicht-geteilte Alias-Adressen mit dem Ziel mindestens einer lokalen Mailbox).",
|
||||||
"spam_alias": "Anpassen temporärer Alias-Adressen",
|
"spam_alias": "Anpassen temporärer Alias-Adressen",
|
||||||
"spam_filter": "Spamfilter",
|
"spam_filter": "Spamfilter",
|
||||||
"spam_policy": "Hinzufügen und Entfernen von Einträgen in White- und Blacklists",
|
"spam_policy": "Hinzufügen und Entfernen von Einträgen in Allow- und Denylisten",
|
||||||
"spam_score": "Einen benutzerdefiniterten Spam-Score festlegen",
|
"spam_score": "Einen benutzerdefiniterten Spam-Score festlegen",
|
||||||
"subfolder2": "Ziel-Ordner<br><small>(leer = kein Unterordner)</small>",
|
"subfolder2": "Ziel-Ordner<br><small>(leer = kein Unterordner)</small>",
|
||||||
"syncjob": "Sync-Job bearbeiten",
|
"syncjob": "Sync-Job bearbeiten",
|
||||||
@@ -804,9 +837,13 @@
|
|||||||
"forgot_password": "> Passwort vergessen?",
|
"forgot_password": "> Passwort vergessen?",
|
||||||
"invalid_pass_reset_token": "Der Rücksetz-Token für das Passwort ist ungültig oder abgelaufen.<br>Bitte fordern Sie einen neuen Link zur Passwortwiederherstellung an.",
|
"invalid_pass_reset_token": "Der Rücksetz-Token für das Passwort ist ungültig oder abgelaufen.<br>Bitte fordern Sie einen neuen Link zur Passwortwiederherstellung an.",
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
"login_user": "Benutzer Anmelden",
|
"login_linkstext": "Nicht der richtige Login?",
|
||||||
"login_dadmin": "Domain-Administrator Anmelden",
|
"login_usertext": "Als Benutzer anmelden",
|
||||||
"login_admin": "Administrator Anmelden",
|
"login_domainadmintext": "Als Domainadmin anmelden",
|
||||||
|
"login_admintext": "Als Admin anmelden",
|
||||||
|
"login_user": "Anmeldung als Benutzer",
|
||||||
|
"login_dadmin": "Anmeldung als Domain-Administrator",
|
||||||
|
"login_admin": "Anmeldung als Administrator",
|
||||||
"mobileconfig_info": "Bitte als Mailbox-Benutzer einloggen, um das Verbindungsprofil herunterzuladen.",
|
"mobileconfig_info": "Bitte als Mailbox-Benutzer einloggen, um das Verbindungsprofil herunterzuladen.",
|
||||||
"new_password": "Neues Passwort",
|
"new_password": "Neues Passwort",
|
||||||
"new_password_confirm": "Neues Passwort bestätigen",
|
"new_password_confirm": "Neues Passwort bestätigen",
|
||||||
@@ -814,7 +851,8 @@
|
|||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"reset_password": "Passwort zurücksetzen",
|
"reset_password": "Passwort zurücksetzen",
|
||||||
"request_reset_password": "Passwortänderung anfordern",
|
"request_reset_password": "Passwortänderung anfordern",
|
||||||
"username": "Benutzername"
|
"username": "Benutzername",
|
||||||
|
"email": "E-Mail-Adresse"
|
||||||
},
|
},
|
||||||
"mailbox": {
|
"mailbox": {
|
||||||
"action": "Aktion",
|
"action": "Aktion",
|
||||||
@@ -835,7 +873,7 @@
|
|||||||
"add_tls_policy_map": "TLS-Richtlinieneintrag hinzufügen",
|
"add_tls_policy_map": "TLS-Richtlinieneintrag hinzufügen",
|
||||||
"address_rewriting": "Adressumschreibung",
|
"address_rewriting": "Adressumschreibung",
|
||||||
"alias": "Alias",
|
"alias": "Alias",
|
||||||
"alias_domain_alias_hint": "Alias-Adressen werden <b>nicht</b> automatisch auch auf Domain-Alias Adressen angewendet. Eine Alias-Adresse <code>mein-alias@domain</code> bildet demnach <b>nicht</b> die Adresse <code>mein-alias@alias-domain</code> ab.<br>E-Mail-Weiterleitungen an externe Postfächer sollten über Sieve (SOGo Weiterleitung oder im Reiter \"Filter\") angelegt werden. Der Button \"Alias über Alias-Domains expandieren\" erstellt fehlende Alias-Adressen in Alias-Domains.",
|
"alias_domain_alias_hint": "Alias-Adressen werden <b>nicht</b> automatisch auch auf Domain-Alias Adressen angewendet. Eine Alias-Adresse <code>mein-alias@domain</code> bildet demnach <b>nicht</b> die Adresse <code>mein-alias@alias-domain</code> ab.<br>E-Mail-Weiterleitungen an externe Postfächer sollten über Sieve (SOGo Weiterleitung oder im Reiter Filter) angelegt werden. Der Button Alias über Alias-Domains expandieren erstellt fehlende Alias-Adressen in Alias-Domains.",
|
||||||
"alias_domain_backupmx": "Alias-Domain für Relay-Domain inaktiv",
|
"alias_domain_backupmx": "Alias-Domain für Relay-Domain inaktiv",
|
||||||
"aliases": "Aliasse",
|
"aliases": "Aliasse",
|
||||||
"allow_from_smtp": "Nur folgende IPs für <b>SMTP</b> erlauben",
|
"allow_from_smtp": "Nur folgende IPs für <b>SMTP</b> erlauben",
|
||||||
@@ -889,6 +927,7 @@
|
|||||||
"in_use": "Prozentualer Gebrauch",
|
"in_use": "Prozentualer Gebrauch",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"insert_preset": "Beispiel \"%s\" laden",
|
"insert_preset": "Beispiel \"%s\" laden",
|
||||||
|
"internal": "Intern",
|
||||||
"kind": "Art",
|
"kind": "Art",
|
||||||
"last_mail_login": "Letzter Mail-Login",
|
"last_mail_login": "Letzter Mail-Login",
|
||||||
"last_modified": "Zuletzt geändert",
|
"last_modified": "Zuletzt geändert",
|
||||||
@@ -926,7 +965,7 @@
|
|||||||
"recipient_map_new": "Neuer Empfänger",
|
"recipient_map_new": "Neuer Empfänger",
|
||||||
"recipient_map_new_info": "Der neue Empfänger muss eine E-Mail-Adresse oder ein Domainname sein.",
|
"recipient_map_new_info": "Der neue Empfänger muss eine E-Mail-Adresse oder ein Domainname sein.",
|
||||||
"recipient_map_old": "Original-Empfänger",
|
"recipient_map_old": "Original-Empfänger",
|
||||||
"recipient_map_old_info": "Der originale Empfänger muss eine E-Mail-Adresse oder ein Domainname sein.",
|
"recipient_map_old_info": "Der originäre Empfänger muss eine E-Mail-Adresse oder ein Domainname sein.",
|
||||||
"recipient_maps": "Empfängerumschreibungen",
|
"recipient_maps": "Empfängerumschreibungen",
|
||||||
"relay_all": "Alle Empfänger-Adressen relayen",
|
"relay_all": "Alle Empfänger-Adressen relayen",
|
||||||
"relay_unknown": "Unbekannte Mailboxen relayen",
|
"relay_unknown": "Unbekannte Mailboxen relayen",
|
||||||
@@ -948,7 +987,7 @@
|
|||||||
"sogo_visible": "Alias Sichtbarkeit in SOGo",
|
"sogo_visible": "Alias Sichtbarkeit in SOGo",
|
||||||
"sogo_visible_n": "Alias in SOGo verbergen",
|
"sogo_visible_n": "Alias in SOGo verbergen",
|
||||||
"sogo_visible_y": "Alias in SOGo anzeigen",
|
"sogo_visible_y": "Alias in SOGo anzeigen",
|
||||||
"spam_aliases": "Temp. Alias",
|
"spam_aliases": "Spam-Alias",
|
||||||
"stats": "Statistik",
|
"stats": "Statistik",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"sync_jobs": "Synchronisationen",
|
"sync_jobs": "Synchronisationen",
|
||||||
@@ -989,7 +1028,8 @@
|
|||||||
"syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung",
|
"syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung",
|
||||||
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
|
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
|
||||||
"syncjob_EXIT_OVERQUOTA": "Ziel Mailbox ist über dem Limit",
|
"syncjob_EXIT_OVERQUOTA": "Ziel Mailbox ist über dem Limit",
|
||||||
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Kann keine Verbindung zum Zielserver herstellen"
|
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Kann keine Verbindung zum Zielserver herstellen",
|
||||||
|
"iam": "Identitätsanbieter"
|
||||||
},
|
},
|
||||||
"oauth2": {
|
"oauth2": {
|
||||||
"access_denied": "Bitte als Mailbox-Nutzer einloggen, um den Zugriff via OAuth2 zu erlauben.",
|
"access_denied": "Bitte als Mailbox-Nutzer einloggen, um den Zugriff via OAuth2 zu erlauben.",
|
||||||
@@ -1021,7 +1061,7 @@
|
|||||||
"notified": "Benachrichtigt",
|
"notified": "Benachrichtigt",
|
||||||
"qhandler_success": "Aktion wurde an das System übergeben. Sie dürfen dieses Fenster nun schließen.",
|
"qhandler_success": "Aktion wurde an das System übergeben. Sie dürfen dieses Fenster nun schließen.",
|
||||||
"qid": "Rspamd QID",
|
"qid": "Rspamd QID",
|
||||||
"qinfo": "Das Quarantänesystem speichert abgelehnte Nachrichten in der Datenbank (dem Sender wird <em>nicht</em> signalisiert, dass seine E-Mail zugestellt wurde) als auch diese, die als Kopie in den Junk-Ordner der jeweiligen Mailbox zugestellt wurden.\r\n <br>\"Als Spam lernen und löschen\" lernt Nachrichten nach bayesscher Statistik als Spam und erstellt Fuzzy Hashes ausgehend von der jeweiligen Nachricht, um ähnliche Inhalte zukünftig zu unterbinden.\r\n <br>Der Prozess des Lernens kann abhängig vom System zeitintensiv sein.<br>Auf Blacklists vorkommende Elemente sind von der Quarantäne ausgeschlossen.",
|
"qinfo": "Das Quarantänesystem speichert abgelehnte Nachrichten in der Datenbank (dem Sender wird <em>nicht</em> signalisiert, dass seine E-Mail zugestellt wurde) als auch diese, die als Kopie in den Junk-Ordner der jeweiligen Mailbox zugestellt wurden.\r\n <br>\"Als Spam lernen und löschen\" lernt Nachrichten nach bayesscher Statistik als Spam und erstellt Fuzzy Hashes ausgehend von der jeweiligen Nachricht, um ähnliche Inhalte zukünftig zu unterbinden.\r\n <br>Der Prozess des Lernens kann abhängig vom System zeitintensiv sein.<br>Auf Denylisten vorkommende Elemente sind von der Quarantäne ausgeschlossen.",
|
||||||
"qitem": "Quarantäneeintrag",
|
"qitem": "Quarantäneeintrag",
|
||||||
"quarantine": "Quarantäne",
|
"quarantine": "Quarantäne",
|
||||||
"quick_actions": "Aktionen",
|
"quick_actions": "Aktionen",
|
||||||
@@ -1060,7 +1100,7 @@
|
|||||||
"legend": "Funktionen der Mailqueue Aktionen:",
|
"legend": "Funktionen der Mailqueue Aktionen:",
|
||||||
"ays": "Soll die derzeitige Queue wirklich komplett bereinigt werden?",
|
"ays": "Soll die derzeitige Queue wirklich komplett bereinigt werden?",
|
||||||
"deliver_mail": "Ausliefern",
|
"deliver_mail": "Ausliefern",
|
||||||
"deliver_mail_legend": "Versucht eine erneute Zustellung der ausgwählten Mails.",
|
"deliver_mail_legend": "Versucht eine erneute Zustellung der ausgewählten Mails.",
|
||||||
"hold_mail": "Zurückhalten",
|
"hold_mail": "Zurückhalten",
|
||||||
"hold_mail_legend": "Hält die ausgewählten Mails zurück. (Verhindert weitere Zustellversuche)",
|
"hold_mail_legend": "Hält die ausgewählten Mails zurück. (Verhindert weitere Zustellversuche)",
|
||||||
"queue_manager": "Queue Manager",
|
"queue_manager": "Queue Manager",
|
||||||
@@ -1092,6 +1132,7 @@
|
|||||||
"bcc_edited": "BCC-Map-Eintrag %s wurde geändert",
|
"bcc_edited": "BCC-Map-Eintrag %s wurde geändert",
|
||||||
"bcc_saved": "BCC- Map-Eintrag wurde gespeichert",
|
"bcc_saved": "BCC- Map-Eintrag wurde gespeichert",
|
||||||
"cors_headers_edited": "CORS Einstellungen wurden erfolgreich gespeichert",
|
"cors_headers_edited": "CORS Einstellungen wurden erfolgreich gespeichert",
|
||||||
|
"custom_login_modified": "Login Anpassung wurde erfolgreich gespeichert",
|
||||||
"db_init_complete": "Datenbankinitialisierung abgeschlossen",
|
"db_init_complete": "Datenbankinitialisierung abgeschlossen",
|
||||||
"delete_filter": "Filter-ID %s wurde gelöscht",
|
"delete_filter": "Filter-ID %s wurde gelöscht",
|
||||||
"delete_filters": "Filter gelöscht: %s",
|
"delete_filters": "Filter gelöscht: %s",
|
||||||
@@ -1229,7 +1270,7 @@
|
|||||||
"delete_ays": "Soll der Löschvorgang wirklich ausgeführt werden?",
|
"delete_ays": "Soll der Löschvorgang wirklich ausgeführt werden?",
|
||||||
"direct_aliases": "Direkte Alias-Adressen",
|
"direct_aliases": "Direkte Alias-Adressen",
|
||||||
"direct_aliases_desc": "Nur direkte Alias-Adressen werden für benutzerdefinierte Einstellungen berücksichtigt.",
|
"direct_aliases_desc": "Nur direkte Alias-Adressen werden für benutzerdefinierte Einstellungen berücksichtigt.",
|
||||||
"direct_protocol_access": "Der Hauptbenutzer hat <b>direkten, externen Zugriff</b> auf folgende Protokolle und Anwendungen. Diese Einstellung wird vom Administrator gesteuert. App-Passwörter können verwendet werden, um individuelle Zugänge für Protokolle und Anwendungen zu erstellen.<br>Der Button \"Webmail\" kann unabhängig der Einstellung immer verwendet werden.",
|
"direct_protocol_access": "Der Hauptbenutzer hat <b>direkten, externen Zugriff</b> auf folgende Protokolle und Anwendungen. Diese Einstellung wird vom Administrator gesteuert. App-Passwörter können verwendet werden, um individuelle Zugänge für Protokolle und Anwendungen zu erstellen.<br>Der Button Webmail kann unabhängig der Einstellung immer verwendet werden.",
|
||||||
"eas_reset": "ActiveSync-Geräte-Cache zurücksetzen",
|
"eas_reset": "ActiveSync-Geräte-Cache zurücksetzen",
|
||||||
"eas_reset_help": "In vielen Fällen kann ein ActiveSync-Profil durch das Zurücksetzen des Caches repariert werden.<br><b>Vorsicht:</b> Alle Elemente werden erneut heruntergeladen!",
|
"eas_reset_help": "In vielen Fällen kann ein ActiveSync-Profil durch das Zurücksetzen des Caches repariert werden.<br><b>Vorsicht:</b> Alle Elemente werden erneut heruntergeladen!",
|
||||||
"eas_reset_now": "Jetzt zurücksetzen",
|
"eas_reset_now": "Jetzt zurücksetzen",
|
||||||
@@ -1240,7 +1281,9 @@
|
|||||||
"encryption": "Verschlüsselung",
|
"encryption": "Verschlüsselung",
|
||||||
"excludes": "Ausschlüsse",
|
"excludes": "Ausschlüsse",
|
||||||
"expire_in": "Ungültig in",
|
"expire_in": "Ungültig in",
|
||||||
|
"expire_never": "Niemals ungültig",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn",
|
||||||
|
"forever": "Für immer",
|
||||||
"force_pw_update": "Das Passwort für diesen Benutzer <b>muss</b> geändert werden, damit die Zugriffssperre auf die Groupware-Komponenten wieder freigeschaltet wird.",
|
"force_pw_update": "Das Passwort für diesen Benutzer <b>muss</b> geändert werden, damit die Zugriffssperre auf die Groupware-Komponenten wieder freigeschaltet wird.",
|
||||||
"from": "von",
|
"from": "von",
|
||||||
"generate": "generieren",
|
"generate": "generieren",
|
||||||
@@ -1305,12 +1348,13 @@
|
|||||||
"sogo_profile_reset": "SOGo-Profil zurücksetzen",
|
"sogo_profile_reset": "SOGo-Profil zurücksetzen",
|
||||||
"sogo_profile_reset_help": "Das Profil wird inklusive <b>aller</b> Kalender- und Kontaktdaten <b>unwiederbringlich gelöscht</b>.",
|
"sogo_profile_reset_help": "Das Profil wird inklusive <b>aller</b> Kalender- und Kontaktdaten <b>unwiederbringlich gelöscht</b>.",
|
||||||
"sogo_profile_reset_now": "Profil jetzt zurücksetzen",
|
"sogo_profile_reset_now": "Profil jetzt zurücksetzen",
|
||||||
"spam_aliases": "Temporäre E-Mail-Aliasse",
|
"spam_aliases": "Spam E-Mail-Aliasse",
|
||||||
|
"spam_aliases_info": "Ein Spam-Alias ist eine temporäre E-Mailadresse, die benutzt werden kann, um eine echte E-Mail Adressen zu schützen. <br>Optional kann eine Ablaufzeit gesetzt werden, sodass der Alias nach dem definierten Zeitraum automatisch deaktiviert wird, was missbrauchte oder geleakte Adressen effektiv entsorgt.",
|
||||||
"spam_score_reset": "Auf Server-Standard zurücksetzen",
|
"spam_score_reset": "Auf Server-Standard zurücksetzen",
|
||||||
"spamfilter": "Spamfilter",
|
"spamfilter": "Spamfilter",
|
||||||
"spamfilter_behavior": "Bewertung",
|
"spamfilter_behavior": "Bewertung",
|
||||||
"spamfilter_bl": "Blacklist",
|
"spamfilter_bl": "Denyliste",
|
||||||
"spamfilter_bl_desc": "Für E-Mail-Adressen, die vom Spamfilter <b>immer</b> als Spam erfasst und abgelehnt werden. Die Quarantäne-Funktion ist für diese Nachrichten deaktiviert. Die Verwendung von Wildcards ist gestattet. Ein Filter funktioniert lediglich für direkte nicht-\"Catch All\" Alias-Adressen (Alias-Adressen mit lediglich einer Mailbox als Ziel-Adresse) sowie die Mailbox-Adresse selbst.",
|
"spamfilter_bl_desc": "Für E-Mail-Adressen, die vom Spamfilter <b>immer</b> als Spam erfasst und abgelehnt werden. Die Quarantäne-Funktion ist für diese Nachrichten <b>deaktiviert</b>. Die Verwendung von Wildcards ist gestattet. Ein Filter funktioniert lediglich für direkte Nicht-„Catch-All“-Alias-Adressen (Alias-Adressen mit lediglich einer Mailbox als Ziel-Adresse) sowie die Mailbox-Adresse selbst.",
|
||||||
"spamfilter_default_score": "Standardwert",
|
"spamfilter_default_score": "Standardwert",
|
||||||
"spamfilter_green": "Grün: Die Nachricht ist kein Spam",
|
"spamfilter_green": "Grün: Die Nachricht ist kein Spam",
|
||||||
"spamfilter_hint": "Der erste Wert beschreibt den \"low spam score\", der zweite Wert den \"high spam score\".",
|
"spamfilter_hint": "Der erste Wert beschreibt den \"low spam score\", der zweite Wert den \"high spam score\".",
|
||||||
@@ -1321,8 +1365,8 @@
|
|||||||
"spamfilter_table_empty": "Keine Einträge vorhanden",
|
"spamfilter_table_empty": "Keine Einträge vorhanden",
|
||||||
"spamfilter_table_remove": "Entfernen",
|
"spamfilter_table_remove": "Entfernen",
|
||||||
"spamfilter_table_rule": "Regel",
|
"spamfilter_table_rule": "Regel",
|
||||||
"spamfilter_wl": "Whitelist",
|
"spamfilter_wl": "Allowliste",
|
||||||
"spamfilter_wl_desc": "Für E-Mail-Adressen, die vom Spamfilter <b>nicht</b> erfasst werden sollen. Die Verwendung von Wildcards ist gestattet. Ein Filter funktioniert lediglich für direkte nicht-\"Catch All\" Alias-Adressen (Alias-Adressen mit lediglich einer Mailbox als Ziel-Adresse) sowie die Mailbox-Adresse selbst.",
|
"spamfilter_wl_desc": "Für E-Mail-Adressen, die vom Spamfilter <b>nicht</b> erfasst werden sollen. Die Verwendung von Wildcards ist gestattet. Ein Filter funktioniert lediglich für direkte Nicht-„Catch-All“-Alias-Adressen (Alias-Adressen mit lediglich einer Mailbox als Ziel-Adresse) sowie die Mailbox-Adresse selbst.",
|
||||||
"spamfilter_yellow": "Gelb: Die Nachricht ist vielleicht Spam, wird als Spam markiert und in den Junk-Ordner verschoben",
|
"spamfilter_yellow": "Gelb: Die Nachricht ist vielleicht Spam, wird als Spam markiert und in den Junk-Ordner verschoben",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"sync_jobs": "Sync Jobs",
|
"sync_jobs": "Sync Jobs",
|
||||||
@@ -1333,7 +1377,7 @@
|
|||||||
"tag_in_subfolder": "In Unterordner",
|
"tag_in_subfolder": "In Unterordner",
|
||||||
"tag_in_subject": "In Betreff",
|
"tag_in_subject": "In Betreff",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"tfa_info": "Zwei-Faktor-Authentifizierung hilft dabei, Ihr Konto zu schützen. Wenn Sie sie aktivieren, benötigen Sie möglicherweise App-Passwörter, um sich bei Apps oder Diensten anzumelden, die die Zwei-Faktor-Authentifizierung nicht unterstützen (z.B. Mailclients).",
|
"tfa_info": "Zwei-Faktor-Authentifizierung hilft dabei, Ihr Konto zu schützen. Wenn Sie sie aktivieren, benötigen Sie App-Passwörter, um sich bei Apps oder Diensten anzumelden, die die Zwei-Faktor-Authentifizierung nicht unterstützen (z.B. Mailclients).",
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
"tls_enforce_in": "TLS eingehend erzwingen",
|
"tls_enforce_in": "TLS eingehend erzwingen",
|
||||||
"tls_enforce_out": "TLS ausgehend erzwingen",
|
"tls_enforce_out": "TLS ausgehend erzwingen",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"sogo_access": "Allow management of SOGo access",
|
"sogo_access": "Allow management of SOGo access",
|
||||||
"sogo_profile_reset": "Reset SOGo profile",
|
"sogo_profile_reset": "Reset SOGo profile",
|
||||||
"spam_alias": "Temporary aliases",
|
"spam_alias": "Temporary aliases",
|
||||||
"spam_policy": "Blacklist/Whitelist",
|
"spam_policy": "Denylist/Allowlist",
|
||||||
"spam_score": "Spam score",
|
"spam_score": "Spam score",
|
||||||
"syncjobs": "Sync jobs",
|
"syncjobs": "Sync jobs",
|
||||||
"tls_policy": "TLS policy",
|
"tls_policy": "TLS policy",
|
||||||
@@ -71,6 +71,8 @@
|
|||||||
"goto_spam": "Learn as <span class=\"text-danger\"><b>spam</b></span>",
|
"goto_spam": "Learn as <span class=\"text-danger\"><b>spam</b></span>",
|
||||||
"hostname": "Host",
|
"hostname": "Host",
|
||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
|
"internal": "Internal",
|
||||||
|
"internal_info": "Internal aliases are only accessible from the own domain or alias domains.",
|
||||||
"kind": "Kind",
|
"kind": "Kind",
|
||||||
"mailbox_quota_def": "Default mailbox quota",
|
"mailbox_quota_def": "Default mailbox quota",
|
||||||
"mailbox_quota_m": "Max. quota per mailbox (MiB)",
|
"mailbox_quota_m": "Max. quota per mailbox (MiB)",
|
||||||
@@ -134,6 +136,7 @@
|
|||||||
"admin_domains": "Domain assignments",
|
"admin_domains": "Domain assignments",
|
||||||
"admins": "Administrators",
|
"admins": "Administrators",
|
||||||
"admins_ldap": "LDAP Administrators",
|
"admins_ldap": "LDAP Administrators",
|
||||||
|
"admin_quicklink": "Hide Quicklink to Admin Login Page",
|
||||||
"advanced_settings": "Advanced settings",
|
"advanced_settings": "Advanced settings",
|
||||||
"allowed_methods": "Access-Control-Allow-Methods",
|
"allowed_methods": "Access-Control-Allow-Methods",
|
||||||
"allowed_origins": "Access-Control-Allow-Origin",
|
"allowed_origins": "Access-Control-Allow-Origin",
|
||||||
@@ -150,7 +153,7 @@
|
|||||||
"arrival_time": "Arrival time (server time)",
|
"arrival_time": "Arrival time (server time)",
|
||||||
"authed_user": "Auth. user",
|
"authed_user": "Auth. user",
|
||||||
"ays": "Are you sure you want to proceed?",
|
"ays": "Are you sure you want to proceed?",
|
||||||
"ban_list_info": "See a list of banned IPs below: <b>network (remaining ban time) - [actions]</b>.<br />IPs queued to be unbanned will be removed from the active ban list within a few seconds.<br />Red labels indicate active permanent bans by blacklisting.",
|
"ban_list_info": "See a list of banned IPs below: <b>network (remaining ban time) - [actions]</b>.<br />IPs queued to be unbanned will be removed from the active ban list within a few seconds.<br />Red labels indicate active permanent bans by denylisting.",
|
||||||
"change_logo": "Change logo",
|
"change_logo": "Change logo",
|
||||||
"logo_normal_label": "Normal",
|
"logo_normal_label": "Normal",
|
||||||
"logo_dark_label": "Inverted for dark mode",
|
"logo_dark_label": "Inverted for dark mode",
|
||||||
@@ -161,6 +164,7 @@
|
|||||||
"credentials_transport_warning": "<b>Warning</b>: Adding a new transport map entry will update the credentials for all entries with a matching next hop column.",
|
"credentials_transport_warning": "<b>Warning</b>: Adding a new transport map entry will update the credentials for all entries with a matching next hop column.",
|
||||||
"customer_id": "Customer ID",
|
"customer_id": "Customer ID",
|
||||||
"customize": "Customize",
|
"customize": "Customize",
|
||||||
|
"login_page": "Login Page",
|
||||||
"destination": "Destination",
|
"destination": "Destination",
|
||||||
"dkim_add_key": "Add ARC/DKIM key",
|
"dkim_add_key": "Add ARC/DKIM key",
|
||||||
"dkim_domains_selector": "Selector",
|
"dkim_domains_selector": "Selector",
|
||||||
@@ -179,6 +183,7 @@
|
|||||||
"domain": "Domain",
|
"domain": "Domain",
|
||||||
"domain_admin": "Domain administrator",
|
"domain_admin": "Domain administrator",
|
||||||
"domain_admins": "Domain administrators",
|
"domain_admins": "Domain administrators",
|
||||||
|
"domainadmin_quicklink": "Hide Quicklink to Domainadmin Login Page",
|
||||||
"domain_s": "Domain/s",
|
"domain_s": "Domain/s",
|
||||||
"duplicate": "Duplicate",
|
"duplicate": "Duplicate",
|
||||||
"duplicate_dkim": "Duplicate DKIM record",
|
"duplicate_dkim": "Duplicate DKIM record",
|
||||||
@@ -187,9 +192,9 @@
|
|||||||
"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_ban_time_increment": "Ban time is incremented with each ban",
|
||||||
"f2b_blacklist": "Blacklisted networks/hosts",
|
"f2b_blacklist": "Denylisted 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 denylisted host or network will always outweigh a allowlist entity. <b>List updates will take a few seconds to be applied.</b>",
|
||||||
"f2b_manage_external": "Manage Fail2Ban externally",
|
"f2b_manage_external": "Manage Fail2Ban externally",
|
||||||
"f2b_manage_external_info": "Fail2ban will still maintain the banlist, but it will not actively set rules to block traffic. Use the generated banlist below to externally block the traffic.",
|
"f2b_manage_external_info": "Fail2ban will still maintain the banlist, but it will not actively set rules to block traffic. Use the generated banlist below to externally block the traffic.",
|
||||||
"f2b_max_attempts": "Max. attempts",
|
"f2b_max_attempts": "Max. attempts",
|
||||||
@@ -199,9 +204,11 @@
|
|||||||
"f2b_parameters": "Fail2ban parameters",
|
"f2b_parameters": "Fail2ban parameters",
|
||||||
"f2b_regex_info": "Logs taken into consideration: SOGo, Postfix, Dovecot, PHP-FPM.",
|
"f2b_regex_info": "Logs taken into consideration: SOGo, Postfix, Dovecot, PHP-FPM.",
|
||||||
"f2b_retry_window": "Retry window (s) for max. attempts",
|
"f2b_retry_window": "Retry window (s) for max. attempts",
|
||||||
"f2b_whitelist": "Whitelisted networks/hosts",
|
"f2b_whitelist": "Allowlisted networks/hosts",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"filter_table": "Filter table",
|
"filter_table": "Filter table",
|
||||||
|
"force_sso_text": "If an external OIDC provider is configured, this option hides the default mailcow login forms and only shows the Single Sign-On button",
|
||||||
|
"force_sso": "Disable mailcow Login and show only Single Sign-On",
|
||||||
"forwarding_hosts": "Forwarding Hosts",
|
"forwarding_hosts": "Forwarding Hosts",
|
||||||
"forwarding_hosts_add_hint": "You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).",
|
"forwarding_hosts_add_hint": "You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).",
|
||||||
"forwarding_hosts_hint": "Incoming messages are unconditionally accepted from any hosts listed here. These hosts are then not checked against DNSBLs or subjected to greylisting. Spam received from them is never rejected, but optionally it can be filed into the Junk folder. The most common use for this is to specify mail servers on which you have set up a rule that forwards incoming emails to your mailcow server.",
|
"forwarding_hosts_hint": "Incoming messages are unconditionally accepted from any hosts listed here. These hosts are then not checked against DNSBLs or subjected to greylisting. Spam received from them is never rejected, but optionally it can be filed into the Junk folder. The most common use for this is to specify mail servers on which you have set up a rule that forwards incoming emails to your mailcow server.",
|
||||||
@@ -229,6 +236,7 @@
|
|||||||
"iam_host": "Host",
|
"iam_host": "Host",
|
||||||
"iam_host_info": "Enter one or more LDAP hosts, separated by commas.",
|
"iam_host_info": "Enter one or more LDAP hosts, separated by commas.",
|
||||||
"iam_import_users": "Import Users",
|
"iam_import_users": "Import Users",
|
||||||
|
"iam_login_provisioning": "Auto-create users on login",
|
||||||
"iam_mapping": "Attribute Mapping",
|
"iam_mapping": "Attribute Mapping",
|
||||||
"iam_bindpass": "Bind Password",
|
"iam_bindpass": "Bind Password",
|
||||||
"iam_periodic_full_sync": "Periodic Full Sync",
|
"iam_periodic_full_sync": "Periodic Full Sync",
|
||||||
@@ -245,7 +253,9 @@
|
|||||||
"iam_username_field": "Username Field",
|
"iam_username_field": "Username Field",
|
||||||
"iam_binddn": "Bind DN",
|
"iam_binddn": "Bind DN",
|
||||||
"iam_use_ssl": "Use SSL",
|
"iam_use_ssl": "Use SSL",
|
||||||
"iam_use_tls": "Use TLS",
|
"iam_use_ssl_info": "If enabling SSL, and port is set to 389, it will be automatically overridden to use 636.",
|
||||||
|
"iam_use_tls": "Use StartTLS",
|
||||||
|
"iam_use_tls_info": "If enabling TLS, you must use the default port for your LDAP server (389). SSL ports cannot be used.",
|
||||||
"iam_version": "Version",
|
"iam_version": "Version",
|
||||||
"ignore_ssl_error": "Ignore SSL Errors",
|
"ignore_ssl_error": "Ignore SSL Errors",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
@@ -271,6 +281,7 @@
|
|||||||
"message": "Message",
|
"message": "Message",
|
||||||
"message_size": "Message size",
|
"message_size": "Message size",
|
||||||
"nexthop": "Next hop",
|
"nexthop": "Next hop",
|
||||||
|
"needs_restart": "needs restart",
|
||||||
"no": "✕",
|
"no": "✕",
|
||||||
"no_active_bans": "No active bans",
|
"no_active_bans": "No active bans",
|
||||||
"no_new_rows": "No further rows available",
|
"no_new_rows": "No further rows available",
|
||||||
@@ -315,6 +326,7 @@
|
|||||||
"quarantine_release_format_att": "As attachment",
|
"quarantine_release_format_att": "As attachment",
|
||||||
"quarantine_release_format_raw": "Unmodified original",
|
"quarantine_release_format_raw": "Unmodified original",
|
||||||
"quarantine_retention_size": "Retentions per mailbox:<br><small>0 indicates <b>inactive</b>.</small>",
|
"quarantine_retention_size": "Retentions per mailbox:<br><small>0 indicates <b>inactive</b>.</small>",
|
||||||
|
"quicklink_text": "Show or hide quick links to other login pages under the login form",
|
||||||
"quota_notification_html": "Notification email template:<br><small>Leave empty to restore default template.</small>",
|
"quota_notification_html": "Notification email template:<br><small>Leave empty to restore default template.</small>",
|
||||||
"quota_notification_sender": "Notification email sender",
|
"quota_notification_sender": "Notification email sender",
|
||||||
"quota_notification_subject": "Notification email subject",
|
"quota_notification_subject": "Notification email subject",
|
||||||
@@ -355,8 +367,8 @@
|
|||||||
"rspamd_com_settings": "A setting name will be auto-generated, please see the example presets below. For more details see <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd docs</a>",
|
"rspamd_com_settings": "A setting name will be auto-generated, please see the example presets below. For more details see <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd docs</a>",
|
||||||
"rspamd_global_filters": "Global filter maps",
|
"rspamd_global_filters": "Global filter maps",
|
||||||
"rspamd_global_filters_agree": "I will be careful!",
|
"rspamd_global_filters_agree": "I will be careful!",
|
||||||
"rspamd_global_filters_info": "Global filter maps contain different kind of global black and whitelists.",
|
"rspamd_global_filters_info": "Global filter maps contain different kind of global deny and allowlists.",
|
||||||
"rspamd_global_filters_regex": "Their names explain their purpose. All content must contain valid regular expression in the format of \"/pattern/options\" (e.g. <code>/.+@domain\\.tld/i</code>).<br>\r\n Although rudimentary checks are being executed on each line of regex, Rspamds functionality can be broken, if it fails to read the syntax correctly.<br>\r\n Rspamd will try to read the map content when changed. If you experience problems, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">restart Rspamd</a> to enforce a map reload.<br>Blacklisted elements are excluded from quarantine.",
|
"rspamd_global_filters_regex": "Their names explain their purpose. All content must contain valid regular expression in the format of \"/pattern/options\" (e.g. <code>/.+@domain\\.tld/i</code>).<br>\r\n Although rudimentary checks are being executed on each line of regex, Rspamds functionality can be broken, if it fails to read the syntax correctly.<br>\r\n Rspamd will try to read the map content when changed. If you experience problems, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">restart Rspamd</a> to enforce a map reload.<br>Denylisted elements are excluded from quarantine.",
|
||||||
"rspamd_settings_map": "Rspamd settings map",
|
"rspamd_settings_map": "Rspamd settings map",
|
||||||
"sal_level": "Moo level",
|
"sal_level": "Moo level",
|
||||||
"save": "Save changes",
|
"save": "Save changes",
|
||||||
@@ -396,6 +408,7 @@
|
|||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"user_link": "User-Link",
|
"user_link": "User-Link",
|
||||||
|
"user_quicklink": "Hide Quicklink to User Login Page",
|
||||||
"validate_license_now": "Validate GUID against license server",
|
"validate_license_now": "Validate GUID against license server",
|
||||||
"verify": "Verify",
|
"verify": "Verify",
|
||||||
"yes": "✓"
|
"yes": "✓"
|
||||||
@@ -472,10 +485,13 @@
|
|||||||
"mailboxes_in_use": "Max. mailboxes must be greater or equal to %d",
|
"mailboxes_in_use": "Max. mailboxes must be greater or equal to %d",
|
||||||
"malformed_username": "Malformed username",
|
"malformed_username": "Malformed username",
|
||||||
"map_content_empty": "Map content cannot be empty",
|
"map_content_empty": "Map content cannot be empty",
|
||||||
|
"max_age_invalid": "Max age %s is invalid",
|
||||||
"max_alias_exceeded": "Max. aliases exceeded",
|
"max_alias_exceeded": "Max. aliases exceeded",
|
||||||
"max_mailbox_exceeded": "Max. mailboxes exceeded (%d of %d)",
|
"max_mailbox_exceeded": "Max. mailboxes exceeded (%d of %d)",
|
||||||
"max_quota_in_use": "Mailbox quota must be greater or equal to %d MiB",
|
"max_quota_in_use": "Mailbox quota must be greater or equal to %d MiB",
|
||||||
"maxquota_empty": "Max. quota per mailbox must not be 0.",
|
"maxquota_empty": "Max. quota per mailbox must not be 0.",
|
||||||
|
"mode_invalid": "Mode %s is invalid",
|
||||||
|
"mx_invalid": "MX record %s is invalid",
|
||||||
"mysql_error": "MySQL error: %s",
|
"mysql_error": "MySQL error: %s",
|
||||||
"network_host_invalid": "Invalid network or host: %s",
|
"network_host_invalid": "Invalid network or host: %s",
|
||||||
"next_hop_interferes": "%s interferes with nexthop %s",
|
"next_hop_interferes": "%s interferes with nexthop %s",
|
||||||
@@ -539,6 +555,7 @@
|
|||||||
"username_invalid": "Username %s cannot be used",
|
"username_invalid": "Username %s cannot be used",
|
||||||
"validity_missing": "Please assign a period of validity",
|
"validity_missing": "Please assign a period of validity",
|
||||||
"value_missing": "Please provide all values",
|
"value_missing": "Please provide all values",
|
||||||
|
"version_invalid": "Version %s is invalid",
|
||||||
"yotp_verification_failed": "Yubico OTP verification failed: %s"
|
"yotp_verification_failed": "Yubico OTP verification failed: %s"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
@@ -675,6 +692,8 @@
|
|||||||
"grant_types": "Grant types",
|
"grant_types": "Grant types",
|
||||||
"hostname": "Hostname",
|
"hostname": "Hostname",
|
||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
|
"internal": "Internal",
|
||||||
|
"internal_info": "Internal aliases are only accessible from the own domain or alias domains.",
|
||||||
"kind": "Kind",
|
"kind": "Kind",
|
||||||
"last_modified": "Last modified",
|
"last_modified": "Last modified",
|
||||||
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
"lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
|
||||||
@@ -693,6 +712,17 @@
|
|||||||
"maxbytespersecond": "Max. bytes per second <br><small>(0 = unlimited)</small>",
|
"maxbytespersecond": "Max. bytes per second <br><small>(0 = unlimited)</small>",
|
||||||
"mbox_rl_info": "This rate limit is applied on the SASL login name, it matches any \"from\" address used by the logged-in user. A mailbox rate limit overrides a domain-wide rate limit.",
|
"mbox_rl_info": "This rate limit is applied on the SASL login name, it matches any \"from\" address used by the logged-in user. A mailbox rate limit overrides a domain-wide rate limit.",
|
||||||
"mins_interval": "Interval (min)",
|
"mins_interval": "Interval (min)",
|
||||||
|
"mta_sts": "MTA-STS",
|
||||||
|
"mta_sts_info": "<a href='https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security' target='_blank'>MTA-STS</a> is a standard that enforces email delivery between mail servers to use TLS with valid certificates. <br>It is used when <a target='_blank' href='https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities'>DANE</a> is not possible due to missing or unsupported DNSSEC.<br><b>Note</b>: If the receiving domain supports DANE with DNSSEC, DANE is <b>always</b> preferred – MTA-STS only acts as a fallback.",
|
||||||
|
"mta_sts_version": "Version",
|
||||||
|
"mta_sts_version_info": "Defines the version of the MTA-STS standard – currently only <code>STSv1</code> is valid.",
|
||||||
|
"mta_sts_mode": "Mode",
|
||||||
|
"mta_sts_mode_info": "There are three modes to choose from:<ul><li><em>testing</em> – policy is only monitored, violations have no impact.</li><li><em>enforce</em> – policy is strictly enforced, connections without valid TLS are rejected.</li><li><em>none</em> – policy is published but not applied.</li></ul>",
|
||||||
|
"mta_sts_max_age": "Max age",
|
||||||
|
"mta_sts_max_age_info": "Time in seconds that receiving mail servers may cache this policy until refetching.",
|
||||||
|
"mta_sts_mx": "MX server",
|
||||||
|
"mta_sts_mx_info": "Allows sending only to explicitly listed mail server hostnames; the sending MTA checks if the DNS MX hostname matches the policy list, and only allows delivery with a valid TLS certificate (guards against MITM).",
|
||||||
|
"mta_sts_mx_notice": "Multiple MX servers can be specified (separated by commas).",
|
||||||
"multiple_bookings": "Multiple bookings",
|
"multiple_bookings": "Multiple bookings",
|
||||||
"none_inherit": "None / Inherit",
|
"none_inherit": "None / Inherit",
|
||||||
"nexthop": "Next hop",
|
"nexthop": "Next hop",
|
||||||
@@ -740,7 +770,7 @@
|
|||||||
"sogo_visible_info": "This option only affects objects, that can be displayed in SOGo (shared or non-shared alias addresses pointing to at least one local mailbox). If hidden, an alias will not appear as selectable sender in SOGo.",
|
"sogo_visible_info": "This option only affects objects, that can be displayed in SOGo (shared or non-shared alias addresses pointing to at least one local mailbox). If hidden, an alias will not appear as selectable sender in SOGo.",
|
||||||
"spam_alias": "Create or change time limited alias addresses",
|
"spam_alias": "Create or change time limited alias addresses",
|
||||||
"spam_filter": "Spam filter",
|
"spam_filter": "Spam filter",
|
||||||
"spam_policy": "Add or remove items to white-/blacklist",
|
"spam_policy": "Add or remove items to allow-/denylist",
|
||||||
"spam_score": "Set a custom spam score",
|
"spam_score": "Set a custom spam score",
|
||||||
"subfolder2": "Sync into subfolder on destination<br><small>(empty = do not use subfolder)</small>",
|
"subfolder2": "Sync into subfolder on destination<br><small>(empty = do not use subfolder)</small>",
|
||||||
"syncjob": "Edit sync job",
|
"syncjob": "Edit sync job",
|
||||||
@@ -807,6 +837,10 @@
|
|||||||
"forgot_password": "> Forgot Password?",
|
"forgot_password": "> Forgot Password?",
|
||||||
"invalid_pass_reset_token": "The reset password token is invalid or has expired.<br>Please request a new password reset link.",
|
"invalid_pass_reset_token": "The reset password token is invalid or has expired.<br>Please request a new password reset link.",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
|
"login_linkstext": "Not the correct login?",
|
||||||
|
"login_usertext": "Log in as user",
|
||||||
|
"login_domainadmintext": "Log in as domain admin",
|
||||||
|
"login_admintext": "Log in as admin",
|
||||||
"login_user": "User Login",
|
"login_user": "User Login",
|
||||||
"login_dadmin": "Domain-Administrator Login",
|
"login_dadmin": "Domain-Administrator Login",
|
||||||
"login_admin": "Administrator Login",
|
"login_admin": "Administrator Login",
|
||||||
@@ -817,7 +851,8 @@
|
|||||||
"password": "Password",
|
"password": "Password",
|
||||||
"reset_password": "Reset Password",
|
"reset_password": "Reset Password",
|
||||||
"request_reset_password": "Request password change",
|
"request_reset_password": "Request password change",
|
||||||
"username": "Username"
|
"username": "Username",
|
||||||
|
"email": "Email address"
|
||||||
},
|
},
|
||||||
"mailbox": {
|
"mailbox": {
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
@@ -897,6 +932,7 @@
|
|||||||
"in_use": "In use (%)",
|
"in_use": "In use (%)",
|
||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
"insert_preset": "Insert example preset \"%s\"",
|
"insert_preset": "Insert example preset \"%s\"",
|
||||||
|
"internal": "Internal",
|
||||||
"kind": "Kind",
|
"kind": "Kind",
|
||||||
"last_mail_login": "Last mail login",
|
"last_mail_login": "Last mail login",
|
||||||
"last_modified": "Last modified",
|
"last_modified": "Last modified",
|
||||||
@@ -1025,7 +1061,7 @@
|
|||||||
"notified": "Notified",
|
"notified": "Notified",
|
||||||
"qhandler_success": "Request successfully sent to the system. You can now close the window.",
|
"qhandler_success": "Request successfully sent to the system. You can now close the window.",
|
||||||
"qid": "Rspamd QID",
|
"qid": "Rspamd QID",
|
||||||
"qinfo": "The quarantine system will save rejected mail to the database (the sender will <em>not</em> be given the impression of a delivered mail) as well as mail, that is delivered as copy into the Junk folder of a mailbox.\r\n <br>\"Learn as spam and delete\" will learn a message as spam via Bayesian theorem and also calculate fuzzy hashes to deny similar messages in the future.\r\n <br>Please be aware that learning multiple messages can be - depending on your system - time consuming.<br>Blacklisted elements are excluded from the quarantine.",
|
"qinfo": "The quarantine system will save rejected mail to the database (the sender will <em>not</em> be given the impression of a delivered mail) as well as mail, that is delivered as copy into the Junk folder of a mailbox.\r\n <br>\"Learn as spam and delete\" will learn a message as spam via Bayesian theorem and also calculate fuzzy hashes to deny similar messages in the future.\r\n <br>Please be aware that learning multiple messages can be - depending on your system - time consuming.<br>Denylisted elements are excluded from the quarantine.",
|
||||||
"qitem": "Quarantine item",
|
"qitem": "Quarantine item",
|
||||||
"quarantine": "Quarantine",
|
"quarantine": "Quarantine",
|
||||||
"quick_actions": "Actions",
|
"quick_actions": "Actions",
|
||||||
@@ -1103,6 +1139,7 @@
|
|||||||
"bcc_edited": "BCC map entry %s edited",
|
"bcc_edited": "BCC map entry %s edited",
|
||||||
"bcc_saved": "BCC map entry saved",
|
"bcc_saved": "BCC map entry saved",
|
||||||
"cors_headers_edited": "CORS settings have been saved",
|
"cors_headers_edited": "CORS settings have been saved",
|
||||||
|
"custom_login_modified": "Login customisation was saved successfully",
|
||||||
"db_init_complete": "Database initialization completed",
|
"db_init_complete": "Database initialization completed",
|
||||||
"delete_filter": "Deleted filters ID %s",
|
"delete_filter": "Deleted filters ID %s",
|
||||||
"delete_filters": "Deleted filters: %s",
|
"delete_filters": "Deleted filters: %s",
|
||||||
@@ -1251,7 +1288,9 @@
|
|||||||
"encryption": "Encryption",
|
"encryption": "Encryption",
|
||||||
"excludes": "Excludes",
|
"excludes": "Excludes",
|
||||||
"expire_in": "Expire in",
|
"expire_in": "Expire in",
|
||||||
|
"expire_never": "Never Expire",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn",
|
||||||
|
"forever": "Forever",
|
||||||
"force_pw_update": "You <b>must</b> set a new password to be able to access groupware related services.",
|
"force_pw_update": "You <b>must</b> set a new password to be able to access groupware related services.",
|
||||||
"from": "from",
|
"from": "from",
|
||||||
"generate": "generate",
|
"generate": "generate",
|
||||||
@@ -1318,12 +1357,13 @@
|
|||||||
"sogo_profile_reset": "Reset SOGo profile",
|
"sogo_profile_reset": "Reset SOGo profile",
|
||||||
"sogo_profile_reset_help": "This will destroy a user's SOGo profile and <b>delete all contact and calendar data irretrievable</b>.",
|
"sogo_profile_reset_help": "This will destroy a user's SOGo profile and <b>delete all contact and calendar data irretrievable</b>.",
|
||||||
"sogo_profile_reset_now": "Reset profile now",
|
"sogo_profile_reset_now": "Reset profile now",
|
||||||
"spam_aliases": "Temporary email aliases",
|
"spam_aliases": "Spam email aliases",
|
||||||
|
"spam_aliases_info": "A spam alias is a temporary email address that can be used to protect real email addresses. <br>Optionally, an expiration time can be set so that the alias is automatically deactivated after the defined period, effectively disposing of abused or leaked addresses.",
|
||||||
"spam_score_reset": "Reset to server default",
|
"spam_score_reset": "Reset to server default",
|
||||||
"spamfilter": "Spam filter",
|
"spamfilter": "Spam filter",
|
||||||
"spamfilter_behavior": "Rating",
|
"spamfilter_behavior": "Rating",
|
||||||
"spamfilter_bl": "Blacklist",
|
"spamfilter_bl": "Denylist",
|
||||||
"spamfilter_bl_desc": "Blacklisted email addresses to <b>always</b> classify as spam and reject. Rejected mail will <b>not</b> be copied to quarantine. Wildcards may be used. A filter is only applied to direct aliases (aliases with a single target mailbox) excluding catch-all aliases and a mailbox itself.",
|
"spamfilter_bl_desc": "Denylisted email addresses to <b>always</b> classify as spam and reject. Rejected mail will <b>not</b> be copied to quarantine. Wildcards may be used. A filter is only applied to direct aliases (aliases with a single target mailbox) excluding catch-all aliases and a mailbox itself.",
|
||||||
"spamfilter_default_score": "Default values",
|
"spamfilter_default_score": "Default values",
|
||||||
"spamfilter_green": "Green: this message is not spam",
|
"spamfilter_green": "Green: this message is not spam",
|
||||||
"spamfilter_hint": "The first value describes the \"low spam score\", the second represents the \"high spam score\".",
|
"spamfilter_hint": "The first value describes the \"low spam score\", the second represents the \"high spam score\".",
|
||||||
@@ -1334,8 +1374,8 @@
|
|||||||
"spamfilter_table_empty": "No data to display",
|
"spamfilter_table_empty": "No data to display",
|
||||||
"spamfilter_table_remove": "remove",
|
"spamfilter_table_remove": "remove",
|
||||||
"spamfilter_table_rule": "Rule",
|
"spamfilter_table_rule": "Rule",
|
||||||
"spamfilter_wl": "Whitelist",
|
"spamfilter_wl": "Allowlist",
|
||||||
"spamfilter_wl_desc": "Whitelisted email addresses are programmed to <b>never</b> classify as spam. Wildcards may be used. A filter is only applied to direct aliases (aliases with a single target mailbox) excluding catch-all aliases and a mailbox itself.",
|
"spamfilter_wl_desc": "Allowlisted email addresses are programmed to <b>never</b> classify as spam. Wildcards may be used. A filter is only applied to direct aliases (aliases with a single target mailbox) excluding catch-all aliases and a mailbox itself.",
|
||||||
"spamfilter_yellow": "Yellow: this message may be spam, will be tagged as spam and moved to your junk folder",
|
"spamfilter_yellow": "Yellow: this message may be spam, will be tagged as spam and moved to your junk folder",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"sync_jobs": "Sync jobs",
|
"sync_jobs": "Sync jobs",
|
||||||
@@ -1355,7 +1395,7 @@
|
|||||||
"tag_in_subfolder": "In subfolder",
|
"tag_in_subfolder": "In subfolder",
|
||||||
"tag_in_subject": "In subject",
|
"tag_in_subject": "In subject",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"tfa_info": "Two-factor authentication helps protect your account. If you enable it, you may need app passwords to log in to apps or services that don't support two-factor authentication (e.g. Mailclients).",
|
"tfa_info": "Two-factor authentication helps protect your account. If you enable it, you need app passwords to log in to apps or services that don't support two-factor authentication (e.g. Mailclients).",
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
"tls_enforce_in": "Enforce TLS incoming",
|
"tls_enforce_in": "Enforce TLS incoming",
|
||||||
"tls_enforce_out": "Enforce TLS outgoing",
|
"tls_enforce_out": "Enforce TLS outgoing",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user