mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-06-13 01:50:34 +00:00
Compare commits
268 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 87214fef70 | |||
| f1f9626b5b | |||
| 3a13c93022 | |||
| 83bd66db98 | |||
| 13175b4e6c | |||
| ecefbf2166 | |||
| a763dda068 | |||
| 698b2bf988 | |||
| 802d304579 | |||
| 8614d63ace | |||
| 6d8c978d17 | |||
| ff4f2ae0b6 | |||
| 0b00f15811 | |||
| bed5218550 | |||
| 86b67a9a7b | |||
| 73370de1f9 | |||
| 524aba0964 | |||
| 64528d8e0e | |||
| 31b5faa729 | |||
| 17977a2fff | |||
| 3e007eeaae | |||
| 05b897f43e | |||
| 738dcac60d | |||
| b3bbeee5e2 | |||
| 782eae4d4c | |||
| f2f5e212f5 | |||
| ff7102468e | |||
| 118cb1017a | |||
| 360bb6f306 | |||
| d8e314db1a | |||
| fd14c51f85 | |||
| 57a5a9baeb | |||
| 65c74c75c7 | |||
| e82f3b3975 | |||
| fbc33da734 | |||
| 210815d4cf | |||
| 1e672ae349 | |||
| 046e658984 | |||
| a46db9e0df | |||
| 17f3cc3ad8 | |||
| 3236a10cf5 | |||
| a4eb6d5f1b | |||
| 05181f1888 | |||
| 6875baf64c | |||
| c46a1c1e2f | |||
| b79a1530fb | |||
| a6a7ab45f8 | |||
| bc937ed2db | |||
| df17e6b75e | |||
| f880e1834d | |||
| 4dd1b97e38 | |||
| aeb433cc39 | |||
| eb9d360c0a | |||
| abfad4e025 | |||
| e9091cbb8c | |||
| cb340d78e1 | |||
| 548d7b9833 | |||
| 96dbbf4db6 | |||
| 4f14462af7 | |||
| 1e08b4ece6 | |||
| 6fd9efc30a | |||
| 6f212a41d8 | |||
| 52314d1a35 | |||
| 3028a18a37 | |||
| 26a5fcf989 | |||
| 509086ef54 | |||
| 963510ed22 | |||
| 2c0f8cda50 | |||
| 50d2671d75 | |||
| b73d879f3c | |||
| 725a5fe5b9 | |||
| 65ca42ca42 | |||
| b22ff59f7b | |||
| 58527857d9 | |||
| 6306c4555c | |||
| 922603f906 | |||
| f8d45de749 | |||
| cb1602c2de | |||
| 8026b6c874 | |||
| 042676fff7 | |||
| 44d53146af | |||
| ce4fb069d5 | |||
| 9444000d46 | |||
| eacd9ac240 | |||
| cf38d6ca69 | |||
| 905993d66e | |||
| 0d7fe2e347 | |||
| 33bd871a63 | |||
| 772122b255 | |||
| 9fb346751c | |||
| cb058e91a3 | |||
| 27e7407407 | |||
| 623397d20a | |||
| 7d46de33d8 | |||
| 5470b51cc7 | |||
| 8e0b1d8aee | |||
| 2834459b22 | |||
| 000894dabd | |||
| 494620cdea | |||
| a502eb239d | |||
| caf775093e | |||
| f28e18e676 | |||
| b4bab1d5b9 | |||
| c4d5072e5c | |||
| 852bf750ca | |||
| 47359c4113 | |||
| 15f2c4c769 | |||
| a0174c61e8 | |||
| 5b30dce609 | |||
| 8f6099e3e4 | |||
| 7c44375223 | |||
| 72e204f8fd | |||
| b5f5b53e37 | |||
| 1c15133a52 | |||
| 7c9c2c35f8 | |||
| 9806e568c0 | |||
| b4bb4e2938 | |||
| de7b809229 | |||
| a40df1ff87 | |||
| a161aa2c92 | |||
| cad0f25345 | |||
| 2ed453a400 | |||
| 452d8a686f | |||
| 90f77f6d5c | |||
| 0c11cf747a | |||
| 6d36475ed3 | |||
| fee6ff43bf | |||
| 57cd5ec818 | |||
| 02512e0f4f | |||
| 555f4a8a6d | |||
| 3633766544 | |||
| e98a984417 | |||
| bc9141753f | |||
| 1f9f4157a6 | |||
| 778a3ed551 | |||
| 5ea4305185 | |||
| ef311f22bf | |||
| e202530afb | |||
| 85deeaf806 | |||
| 825c8a6abe | |||
| cdc8f63b4b | |||
| 9db9818ede | |||
| 4f7ee669d3 | |||
| 77f9947613 | |||
| a8eb3b6ac5 | |||
| 575eab1cf0 | |||
| 7a23e4fd4e | |||
| b16b276f36 | |||
| 4f380debb5 | |||
| f34d3620b1 | |||
| 70e99447f9 | |||
| 047c4aa3a0 | |||
| 925b220905 | |||
| 6708059227 | |||
| 1f3d9d4e1c | |||
| 0dcfac8f15 | |||
| ad8b7f0894 | |||
| 55f810b23f | |||
| 65eddee63e | |||
| 7b57b3392c | |||
| 492451bfee | |||
| 4322c98f73 | |||
| edcf789126 | |||
| b985ba4f0e | |||
| 67c0405274 | |||
| 9b32151ab5 | |||
| a1e8077f45 | |||
| 956e4e2aa7 | |||
| b51a659515 | |||
| 44a6f09a09 | |||
| 4c10525078 | |||
| c9ab8b2eff | |||
| 4bf38bf00f | |||
| 7c7c67948e | |||
| 263cb96786 | |||
| b6e3e7a658 | |||
| ceaf1423f4 | |||
| c2e0a275e1 | |||
| c8620a066d | |||
| 9598b503ec | |||
| 1ca566f670 | |||
| 94f4ec8b96 | |||
| 7aab2c55ff | |||
| 6abb4d34c1 | |||
| c8ccf080f3 | |||
| 0342ae926c | |||
| be08742653 | |||
| 528f7da5ef | |||
| 7d72ae3449 | |||
| 753cde0b85 | |||
| 223ba44b61 | |||
| cd02483b19 | |||
| f724662874 | |||
| bee762737e | |||
| 83efd3e506 | |||
| 2278a6cc73 | |||
| 586b60b276 | |||
| f07b9ea304 | |||
| 09dca5d76c | |||
| 65bb808441 | |||
| 83b79edb42 | |||
| b8ec244d92 | |||
| 5b924614aa | |||
| 43103add47 | |||
| 124d5d6bb2 | |||
| 58fde558f7 | |||
| 8b314acfcf | |||
| 1c0eab9893 | |||
| 514079fe96 | |||
| c62daa0c59 | |||
| 1a05101f50 | |||
| 47fb46c837 | |||
| d29580aa02 | |||
| d0fc62ef13 | |||
| b14c0e4c11 | |||
| 43ec12f4f0 | |||
| 40cf2c85e6 | |||
| 6195b7c334 | |||
| 385570c1e8 | |||
| d82cfc6c62 | |||
| 27e9210d52 | |||
| fdf52dcb17 | |||
| 1ff220ccf8 | |||
| 1b6e5b7116 | |||
| 536ab34955 | |||
| f7369f0611 | |||
| 14bc105d43 | |||
| 2efb4365bf | |||
| c1b86fc782 | |||
| 52e92cc0db | |||
| 3af2f636a5 | |||
| 6fb967cf79 | |||
| 0dab215e27 | |||
| 03c49ea1f8 | |||
| 6ec136e63f | |||
| bba5671eaf | |||
| 88d7593d89 | |||
| bd23b80d45 | |||
| f96e0c4071 | |||
| 11700d7ecb | |||
| 33eb2c8801 | |||
| 537a7908f1 | |||
| 3fe776ee69 | |||
| 581be02e53 | |||
| 0eb2545773 | |||
| 7d5990bf0f | |||
| 4ec982163e | |||
| 3c9502f241 | |||
| 63cecb2fd8 | |||
| 3029a2d33d | |||
| fa0d2a959d | |||
| 6d3798ad08 | |||
| 70921b8d15 | |||
| b185f83fc3 | |||
| 60af295c0a | |||
| e7fe52a625 | |||
| 49c506eed9 | |||
| 21fadf6df2 | |||
| 5fcccbc97d | |||
| 3ef2b6cfa2 | |||
| 84b4269c75 | |||
| a2d57d43d1 | |||
| df33f1a130 | |||
| 4c6a2055c2 | |||
| f09a3df870 | |||
| ea1a412749 | |||
| db82327d9a | |||
| ea1a02bd7d |
-120
@@ -1,120 +0,0 @@
|
|||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: integration-testing
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
clone:
|
|
||||||
disable: true
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: prepare-tests
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- git clone https://github.com/mailcow/mailcow-integration-tests.git --branch $(curl -sL https://api.github.com/repos/mailcow/mailcow-integration-tests/releases/latest | jq -r '.tag_name') --single-branch .
|
|
||||||
- chmod +x ci.sh
|
|
||||||
- chmod +x ci-ssh.sh
|
|
||||||
- chmod +x ci-piprequierments.sh
|
|
||||||
- ./ci.sh
|
|
||||||
- wget -O group_vars/all/secrets.yml $SECRETS_DOWNLOAD_URL --quiet
|
|
||||||
environment:
|
|
||||||
SECRETS_DOWNLOAD_URL:
|
|
||||||
from_secret: SECRETS_DOWNLOAD_URL
|
|
||||||
VAULT_PW:
|
|
||||||
from_secret: VAULT_PW
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: lint
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- ansible-lint ./
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: create-server
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- ./ci-piprequierments.sh
|
|
||||||
- ansible-playbook mailcow-start-server.yml --diff
|
|
||||||
- ./ci-ssh.sh
|
|
||||||
environment:
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: false
|
|
||||||
ANSIBLE_FORCE_COLOR: true
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: setup-server
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- sleep 120
|
|
||||||
- ./ci-piprequierments.sh
|
|
||||||
- ansible-playbook mailcow-setup-server.yml --private-key /drone/src/id_ssh_rsa --diff
|
|
||||||
environment:
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: false
|
|
||||||
ANSIBLE_FORCE_COLOR: true
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: run-tests
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- ./ci-piprequierments.sh
|
|
||||||
- ansible-playbook mailcow-integration-tests.yml --private-key /drone/src/id_ssh_rsa --diff
|
|
||||||
environment:
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: false
|
|
||||||
ANSIBLE_FORCE_COLOR: true
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: delete-server
|
|
||||||
pull: default
|
|
||||||
image: timovibritannia/ansible
|
|
||||||
commands:
|
|
||||||
- ./ci-piprequierments.sh
|
|
||||||
- ansible-playbook mailcow-delete-server.yml --diff
|
|
||||||
environment:
|
|
||||||
ANSIBLE_HOST_KEY_CHECKING: false
|
|
||||||
ANSIBLE_FORCE_COLOR: true
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
status:
|
|
||||||
- failure
|
|
||||||
- success
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: signature
|
|
||||||
hmac: f6619243fe2a27563291c9f2a46d93ffbc3b6dced9a05f23e64b555ce03a31e5
|
|
||||||
|
|
||||||
...
|
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
custom: https://mailcow.github.io/mailcow-dockerized-docs/#help-mailcow
|
custom: ["https://www.servercow.de/mailcow?lang=en#sal"]
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ body:
|
|||||||
label: Contribution guidelines
|
label: Contribution guidelines
|
||||||
description: Please read the contribution guidelines before proceeding.
|
description: Please read the contribution guidelines before proceeding.
|
||||||
options:
|
options:
|
||||||
- label: I've read the [contribution guidelines](https://github.com/mailcow/mailcow-dockerized/blob/master/CONTRIBUTING.md) and wholeheartedly agree
|
- label: I've read the [contribution guidelines](https://github.com/mailcow/mailcow-dockerized/blob/master/CONTRIBUTING.md) and wholeheartedly agree
|
||||||
required: true
|
required: true
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: I've found a bug and checked that ...
|
label: I've found a bug and checked that ...
|
||||||
@@ -26,70 +26,132 @@ body:
|
|||||||
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 in 1-2 sentences. If applicable, add screenshots to help explain your problem. Very useful for bugs in mailcow UI.
|
||||||
|
render: text
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Logs
|
label: "Logs:"
|
||||||
description: Please take a look at the [official documentation](https://mailcow.github.io/mailcow-dockerized-docs/debug-logs/) and post the last few lines of logs, when the error occurs. For example, docker container logs of affected containers. This will be automatically formatted into code, so no need for backticks.
|
description: "Please take a look at the [official documentation](https://docs.mailcow.email/troubleshooting/debug-logs/) and post the last few lines of logs, when the error occurs. For example, docker container logs of affected containers. This will be automatically formatted into code, so no need for backticks."
|
||||||
render: bash
|
render: text
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Steps to reproduce
|
label: "Steps to reproduce:"
|
||||||
description: Please describe the steps to reproduce the bug. Screenshots can be added, if helpful.
|
description: "Please describe the steps to reproduce the bug. Screenshots can be added, if helpful."
|
||||||
|
render: text
|
||||||
placeholder: |-
|
placeholder: |-
|
||||||
1. ...
|
1. ...
|
||||||
2. ...
|
2. ...
|
||||||
3. ...
|
3. ...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
label: System information
|
value: |
|
||||||
description: In this stage we would kindly ask you to attach general system information about your setup.
|
## System information
|
||||||
value: |-
|
### In this stage we would kindly ask you to attach general system information about your setup.
|
||||||
| Question | Answer |
|
- type: dropdown
|
||||||
| --- | --- |
|
attributes:
|
||||||
| My operating system | I_DO_REPLY_HERE |
|
label: "Which branch are you using?"
|
||||||
| Is Apparmor, SELinux or similar active? | I_DO_REPLY_HERE |
|
description: "#### `git rev-parse --abbrev-ref HEAD`"
|
||||||
| Virtualization technology (KVM, VMware, Xen, etc - **LXC and OpenVZ are not supported** | I_DO_REPLY_HERE |
|
multiple: false
|
||||||
| Server/VM specifications (Memory, CPU Cores) | I_DO_REPLY_HERE |
|
options:
|
||||||
| Docker version (`docker version`) | I_DO_REPLY_HERE |
|
- master
|
||||||
| docker-compose version (`docker-compose version`) | I_DO_REPLY_HERE |
|
- nightly
|
||||||
| mailcow version (```git describe --tags `git rev-list --tags --max-count=1` ```) | I_DO_REPLY_HERE |
|
validations:
|
||||||
| Reverse proxy (custom solution) | I_DO_REPLY_HERE |
|
required: true
|
||||||
|
- type: input
|
||||||
Output of `git diff origin/master`, any other changes to the code? If so, **please post them**:
|
attributes:
|
||||||
```
|
label: "Operating System:"
|
||||||
YOUR OUTPUT GOES HERE
|
placeholder: "e.g. Ubuntu 22.04 LTS"
|
||||||
```
|
validations:
|
||||||
|
required: true
|
||||||
All third-party firewalls and custom iptables rules are unsupported. **Please check the Docker docs about how to use Docker with your own ruleset**. Nevertheless, iptabels output can help us to help you:
|
- type: input
|
||||||
iptables -L -vn:
|
attributes:
|
||||||
```
|
label: "Server/VM specifications:"
|
||||||
YOUR OUTPUT GOES HERE
|
placeholder: "Memory, CPU Cores"
|
||||||
```
|
validations:
|
||||||
|
required: true
|
||||||
ip6tables -L -vn:
|
- type: input
|
||||||
```
|
attributes:
|
||||||
YOUR OUTPUT GOES HERE
|
label: "Is Apparmor, SELinux or similar active?"
|
||||||
```
|
placeholder: "yes/no"
|
||||||
|
validations:
|
||||||
iptables -L -vn -t nat:
|
required: true
|
||||||
```
|
- type: input
|
||||||
YOUR OUTPUT GOES HERE
|
attributes:
|
||||||
```
|
label: "Virtualization technology:"
|
||||||
|
placeholder: "KVM, VMware, Xen, etc - **LXC and OpenVZ are not supported**"
|
||||||
ip6tables -L -vn -t nat:
|
validations:
|
||||||
```
|
required: true
|
||||||
YOUR OUTPUT GOES HERE
|
- type: input
|
||||||
```
|
attributes:
|
||||||
|
label: "Docker version:"
|
||||||
DNS problems? Please run `docker exec -it $(docker ps -qf name=acme-mailcow) dig +short stackoverflow.com @172.22.1.254` (set the IP accordingly, if you changed the internal mailcow network) and post the output:
|
description: "#### `docker version`"
|
||||||
```
|
placeholder: "20.10.21"
|
||||||
YOUR OUTPUT GOES HERE
|
validations:
|
||||||
```
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: "docker-compose version or docker compose version:"
|
||||||
|
description: "#### `docker-compose version` or `docker compose version`"
|
||||||
|
placeholder: "v2.12.2"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: "mailcow version:"
|
||||||
|
description: "#### ```git describe --tags `git rev-list --tags --max-count=1` ```"
|
||||||
|
placeholder: "2022-08"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: "Reverse proxy:"
|
||||||
|
placeholder: "e.g. Nginx/Traefik"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: "Logs of git diff:"
|
||||||
|
description: "#### Output of `git diff origin/master`, any other changes to the code? If so, **please post them**:"
|
||||||
|
render: text
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: "Logs of iptables -L -vn:"
|
||||||
|
description: "#### Output of `iptables -L -vn`"
|
||||||
|
render: text
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: "Logs of ip6tables -L -vn:"
|
||||||
|
description: "#### Output of `ip6tables -L -vn`"
|
||||||
|
render: text
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: "Logs of iptables -L -vn -t nat:"
|
||||||
|
description: "#### Output of `iptables -L -vn -t nat`"
|
||||||
|
render: text
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: "Logs of ip6tables -L -vn -t nat:"
|
||||||
|
description: "#### Output of `ip6tables -L -vn -t nat`"
|
||||||
|
render: text
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: "DNS check:"
|
||||||
|
description: "#### Output of `docker exec -it $(docker ps -qf name=acme-mailcow) dig +short stackoverflow.com @172.22.1.254` (set the IP accordingly, if you changed the internal mailcow network)"
|
||||||
|
render: text
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
## :memo: Brief description
|
||||||
|
<!-- Diff summary - START -->
|
||||||
|
<!-- Diff summary - END -->
|
||||||
|
|
||||||
|
|
||||||
|
## :computer: Commits
|
||||||
|
<!-- Diff commits - START -->
|
||||||
|
<!-- Diff commits - END -->
|
||||||
|
|
||||||
|
|
||||||
|
## :file_folder: Modified files
|
||||||
|
<!-- Diff files - START -->
|
||||||
|
<!-- Diff files - END -->
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
@@ -0,0 +1,33 @@
|
|||||||
|
name: Check PRs if on staging
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened, edited]
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
is_not_staging:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event.pull_request.base.ref != 'staging' #check if the target branch is not staging
|
||||||
|
steps:
|
||||||
|
- name: Send message
|
||||||
|
uses: thollander/actions-comment-pull-request@main
|
||||||
|
with:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
|
||||||
|
message: |
|
||||||
|
Thanks for contributing!
|
||||||
|
|
||||||
|
I noticed that you didn't select `staging` as your base branch. Please change the base branch to `staging`.
|
||||||
|
See the attached picture on how to change the base branch to `staging`:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- name: Fail #we want to see failed checks in the PR
|
||||||
|
if: ${{ success() }} #set exit code to 1 even if commenting somehow failed
|
||||||
|
run: exit 1
|
||||||
|
|
||||||
|
is_staging:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event.pull_request.base.ref == 'staging' #check if the target branch is staging
|
||||||
|
steps:
|
||||||
|
- name: Success
|
||||||
|
run: exit 0
|
||||||
@@ -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@v5.0.0
|
uses: actions/stale@v6.0.1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
||||||
days-before-stale: 60
|
days-before-stale: 60
|
||||||
@@ -30,6 +30,7 @@ jobs:
|
|||||||
stale-issue-label: "stale"
|
stale-issue-label: "stale"
|
||||||
stale-pr-label: "stale"
|
stale-pr-label: "stale"
|
||||||
exempt-draft-pr: "true"
|
exempt-draft-pr: "true"
|
||||||
|
close-issue-reason: "not_planned"
|
||||||
operations-per-run: "250"
|
operations-per-run: "250"
|
||||||
ascending: "true"
|
ascending: "true"
|
||||||
#DRY-RUN
|
#DRY-RUN
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
name: Build mailcow Docker Images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master", "staging" ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker_image_builds:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
images:
|
||||||
|
- "acme-mailcow"
|
||||||
|
- "clamd-mailcow"
|
||||||
|
- "dockerapi-mailcow"
|
||||||
|
- "dovecot-mailcow"
|
||||||
|
- "netfilter-mailcow"
|
||||||
|
- "olefy-mailcow"
|
||||||
|
- "php-fpm-mailcow"
|
||||||
|
- "postfix-mailcow"
|
||||||
|
- "rspamd-mailcow"
|
||||||
|
- "sogo-mailcow"
|
||||||
|
- "solr-mailcow"
|
||||||
|
- "unbound-mailcow"
|
||||||
|
- "watchdog-mailcow"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup Docker
|
||||||
|
run: |
|
||||||
|
curl -sSL https://get.docker.com/ | CHANNEL=stable sudo sh
|
||||||
|
sudo service docker start
|
||||||
|
sudo curl -L https://github.com/docker/compose/releases/download/v$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose
|
||||||
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
- name: Prepair Image Builds
|
||||||
|
run: |
|
||||||
|
cp helper-scripts/docker-compose.override.yml.d/BUILD_FLAGS/docker-compose.override.yml docker-compose.override.yml
|
||||||
|
- name: Build Docker Images
|
||||||
|
run: |
|
||||||
|
docker-compose build ${image}
|
||||||
|
env:
|
||||||
|
image: ${{ matrix.images }}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
name: mailcow Integration Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master", "staging" ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
integration_tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Setup Ansible
|
||||||
|
run: |
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install python3 python3-pip git
|
||||||
|
sudo pip3 install ansible
|
||||||
|
- name: Prepair Test Environment
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/mailcow/mailcow-integration-tests.git --branch $(curl -sL https://api.github.com/repos/mailcow/mailcow-integration-tests/releases/latest | jq -r '.tag_name') --single-branch .
|
||||||
|
./fork_check.sh
|
||||||
|
./ci.sh
|
||||||
|
./ci-pip-requirements.sh
|
||||||
|
env:
|
||||||
|
VAULT_PW: ${{ secrets.MAILCOW_TESTS_VAULT_PW }}
|
||||||
|
VAULT_FILE: ${{ secrets.MAILCOW_TESTS_VAULT_FILE }}
|
||||||
|
- name: Start Integration Test Server
|
||||||
|
run: |
|
||||||
|
./fork_check.sh
|
||||||
|
ansible-playbook mailcow-start-server.yml --diff
|
||||||
|
env:
|
||||||
|
PY_COLORS: '1'
|
||||||
|
ANSIBLE_FORCE_COLOR: '1'
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
||||||
|
- name: Setup Integration Test Server
|
||||||
|
run: |
|
||||||
|
./fork_check.sh
|
||||||
|
sleep 30
|
||||||
|
ansible-playbook mailcow-setup-server.yml --private-key id_ssh_rsa --diff
|
||||||
|
env:
|
||||||
|
PY_COLORS: '1'
|
||||||
|
ANSIBLE_FORCE_COLOR: '1'
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
||||||
|
- name: Run Integration Tests
|
||||||
|
run: |
|
||||||
|
./fork_check.sh
|
||||||
|
ansible-playbook mailcow-integration-tests.yml --private-key id_ssh_rsa --diff
|
||||||
|
env:
|
||||||
|
PY_COLORS: '1'
|
||||||
|
ANSIBLE_FORCE_COLOR: '1'
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
||||||
|
- name: Delete Integration Test Server
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
./fork_check.sh
|
||||||
|
ansible-playbook mailcow-delete-server.yml --diff
|
||||||
|
env:
|
||||||
|
PY_COLORS: '1'
|
||||||
|
ANSIBLE_FORCE_COLOR: '1'
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: 'false'
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: Create PR to merge to nightly from staging
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- staging
|
||||||
|
jobs:
|
||||||
|
action-pull-request:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Run the Action
|
||||||
|
uses: devops-infra/action-pull-request@v0.5.3
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
||||||
|
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
||||||
|
assignee: DerLinkman
|
||||||
|
source_branch: staging
|
||||||
|
target_branch: nightly
|
||||||
|
reviewer: DerLinkman
|
||||||
|
label: upstream
|
||||||
|
template: .github/ISSUE_TEMPLATE/pr_to_nighty_template.yml
|
||||||
|
get_diff: true
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Build mailcow backup image
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# At 00:00 on Sunday
|
||||||
|
- cron: "0 0 * * 0"
|
||||||
|
workflow_dispatch: # Allow to run workflow manually
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker_image_build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: data/Dockerfiles/backup/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: mailcow/backup:latest
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
name: "Tweet trigger release"
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tweet:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: "Get Release Tag"
|
||||||
|
run: |
|
||||||
|
RELEASE_TAG=$(curl https://api.github.com/repos/mailcow/mailcow-dockerized/releases/latest | jq -r '.tag_name')
|
||||||
|
- name: Tweet-trigger-publish-release
|
||||||
|
uses: mugi111/tweet-trigger-release@v1.1
|
||||||
|
with:
|
||||||
|
consumer_key: ${{ secrets.CONSUMER_KEY }}
|
||||||
|
consumer_secret: ${{ secrets.CONSUMER_SECRET }}
|
||||||
|
access_token_key: ${{ secrets.ACCESS_TOKEN_KEY }}
|
||||||
|
access_token_secret: ${{ secrets.ACCESS_TOKEN_SECRET }}
|
||||||
|
tweet_body: 'A new mailcow update has just been released! Checkout the GitHub Page for changelog and more informations: https://github.com/mailcow/mailcow-dockerized/releases/latest'
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
sudo: required
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
script:
|
|
||||||
- echo 'Europe/Berlin' | MAILCOW_HOSTNAME=build.mailcow ./generate_config.sh
|
|
||||||
- docker-compose pull --ignore-pull-failures --parallel
|
|
||||||
- docker-compose build
|
|
||||||
- docker login --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD
|
|
||||||
- docker-compose push
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master_disabled
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- secure: MpxpTwD7f0CNEVLitSpVmocK7O9r+BwFE1deEHK4AlQo/oc9cOlhGe1EL3mx9zbglPmjlDg/8kMUGv6vSirIabfBo9Szjps76bHckFr9lr2Ykkg0e29oC8pgPpSXD1eY/1ZIN/FvIkxpUFLETo1okS/j9q/A0DCGFmti0n3EoMORsgRz9CpNAiEh0zpSd6+euPAGHuczuCrDuO84my9bIOCjA/+aPunHNeXiuM8yIM2SxCSyGtIKT0+jvquIvLF58VxivysXBlRfhDn8fhB09nXA2Ru/derYQACfcmNSn9Pd4bDpebPJW5B9H/XA8xjb58uKinUlncbAMB/QnxoT75j9YRWJZRSQ+34XNYP6ZgK9soZ2TC6djQyEKTUu45Kp/1s+poSn42m9jytJJTmmK0KxsZTRcC8JD5nrjIMZWPUNNTwC5L4+I7ZRWg2WooK3LNyq1Ng8Hn6W77wSgsvAJw2HD3Lx58AprGUhHuBeaIZRuSN9aKwZrl9vKQJLqPnOp/nF2EC6kot5HYYtcotGtETXPUDih21gWD5ZM2BqVqYfQQnJnNMgeYmMdj6QQuTFqhuNJf7hXRIRkTnD3j1gDOLKQZazW0+N2JE8XWDFwi6fKScDsxT85lJti9HmzHa7+k4RVHmUYuDgRoPuzUgjWHvPsiz3/Z8WQ9JYpH84S8w=
|
|
||||||
- secure: fWzZisT6nGDNL4lf6tXB07eFG2drgBakHxzdF/NFVvzuP861RFR6omuL+ED0PgXrEHDJBxaBLv52je8irmUXrAH1CNr7T8DWiZo/h5h609Uzr+38T1NnIu4krL0Wo6/CDwlLKnzqTq9yBIZLQSHVJmo8AOpo1JPIi2ajodqj9ZfmAxDQTQl+G6zvQjtqIkYHsHY7A44Rto0f14ykn7w2S82Jn6Ry89VNI5V1WEO3sMpM/XekNP/HokNcRIuntL/0+kuLvTJ5akGoTjBQxSnSW95opzPeGky74HRU2obExJYqKvF0VfVJRNAqejwjIiFIbbjqV0Sk5391kFuhuBErQQDM1bOHGdxZ41HsJH29qNWIl7C33Yl10qERoqecgsJ1N/bS2ZEmWqm/zQh5GClCXPvYmzEqMYsMGM3vjbKdjDlc1Wh2w/eFclsXN9LSXh1mc35rtj46frcT6e5Kof87AIfC9hTgDvk9kAsyjaHMkSHSZthbZXCIcsD8qriNm5UqfFBYD79mPIP1S2YMQ2jscCsjHOZgYVrcm0kzDF21J1w6H0Lo7d1jw37LYlegBdtLQ9gYgqY2D5m+nxWuVoD5FZmpR+5JGtK+ootyLFF8aiFoHXd4op1JCxRLjgkmnZKXzw3kTQSpE7oa7CgzchtQmK2nqcqla1b5Qk7ilVcjooo=
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
||||||
|
|
||||||
## We stand with 🇺🇦
|
[](https://github.com/mailcow/mailcow-dockerized/actions/workflows/integration_tests.yml)
|
||||||
|
[](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
||||||
[](https://drone.mailcow.email/mailcow/mailcow-dockerized) [](https://drone.mailcow.email/mailcow/mailcow-dockerized) [](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
|
||||||
[](https://twitter.com/mailcow_email)
|
[](https://twitter.com/mailcow_email)
|
||||||
|
|
||||||
## Want to support mailcow?
|
## Want to support mailcow?
|
||||||
@@ -35,3 +34,9 @@ Telegram desktop clients are available for [multiple platforms](https://desktop.
|
|||||||
|
|
||||||
**Important**: mailcow makes use of various open-source software. Please assure you agree with their license before using mailcow.
|
**Important**: mailcow makes use of various open-source software. Please assure you agree with their license before using mailcow.
|
||||||
Any part of mailcow itself is released under **GNU General Public License, Version 3**.
|
Any part of mailcow itself is released under **GNU General Public License, Version 3**.
|
||||||
|
|
||||||
|
mailcow is a registered word mark of The Infrastructure Company GmbH, Parkstr. 42, 47877 Willich, Germany.
|
||||||
|
|
||||||
|
The project is managed and maintained by The Infrastructure Company GmbH.
|
||||||
|
|
||||||
|
Originated from @andryyy (André)
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
# Security Policies and Procedures
|
||||||
|
|
||||||
|
This document outlines security procedures and general policies for the _mailcow: dockerized_ project as found on [mailcow-dockerized](https://github.com/mailcow/mailcow-dockerized).
|
||||||
|
|
||||||
|
* [Reporting a Vulnerability](#reporting-a-vulnerability)
|
||||||
|
* [Disclosure Policy](#disclosure-policy)
|
||||||
|
* [Comments on this Policy](#comments-on-this-policy)
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
The mailcow team and community take all security vulnerabilities
|
||||||
|
seriously. Thank you for improving the security of our open source
|
||||||
|
software. We appreciate your efforts and responsible disclosure and will
|
||||||
|
make every effort to acknowledge your contributions.
|
||||||
|
|
||||||
|
Report security vulnerabilities by emailing the mailcow team at:
|
||||||
|
|
||||||
|
info at servercow.de
|
||||||
|
|
||||||
|
mailcow team will acknowledge your email as soon as possible, and will
|
||||||
|
send a more detailed response afterwards indicating the next steps in
|
||||||
|
handling your report. After the initial reply to your report, the mailcow
|
||||||
|
team will endeavor to keep you informed of the progress towards a fix and
|
||||||
|
full announcement, and may ask for additional information or guidance.
|
||||||
|
|
||||||
|
Report security vulnerabilities in third-party modules to the person or
|
||||||
|
team maintaining the module.
|
||||||
|
|
||||||
|
## Disclosure Policy
|
||||||
|
|
||||||
|
When the mailcow team receives a security bug report, they will assign it
|
||||||
|
to a primary handler. This person will coordinate the fix and release
|
||||||
|
process, involving the following steps:
|
||||||
|
|
||||||
|
* Confirm the problem and determine the affected versions.
|
||||||
|
* Audit code to find any potential similar problems.
|
||||||
|
* Prepare fixes for all releases still under maintenance.
|
||||||
|
|
||||||
|
## Comments on this Policy
|
||||||
|
|
||||||
|
If you have suggestions on how this process could be improved please submit a
|
||||||
|
pull request.
|
||||||
Regular → Executable
@@ -0,0 +1,3 @@
|
|||||||
|
FROM debian:bullseye-slim
|
||||||
|
|
||||||
|
RUN apt update && apt install pigz
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM clamav/clamav:0.105.0_base
|
FROM clamav/clamav:0.105.1_base
|
||||||
|
|
||||||
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
@@ -8,8 +8,14 @@ RUN apk upgrade --no-cache \
|
|||||||
bind-tools \
|
bind-tools \
|
||||||
bash
|
bash
|
||||||
|
|
||||||
COPY clamd.sh ./
|
# init
|
||||||
|
COPY clamd.sh /clamd.sh
|
||||||
RUN chmod +x /sbin/tini
|
RUN chmod +x /sbin/tini
|
||||||
|
|
||||||
|
# healthcheck
|
||||||
|
COPY healthcheck.sh /healthcheck.sh
|
||||||
|
RUN chmod +x /healthcheck.sh
|
||||||
|
HEALTHCHECK --start-period=6m CMD "/healthcheck.sh"
|
||||||
|
|
||||||
ENTRYPOINT []
|
ENTRYPOINT []
|
||||||
CMD ["/sbin/tini", "-g", "--", "/clamd.sh"]
|
CMD ["/sbin/tini", "-g", "--", "/clamd.sh"]
|
||||||
Executable
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ "${SKIP_CLAMD}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
|
echo "SKIP_CLAMD=y, skipping ClamAV..."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run clamd healthcheck
|
||||||
|
/usr/local/bin/clamdcheck.sh
|
||||||
@@ -2,7 +2,7 @@ FROM debian:bullseye-slim
|
|||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ARG DOVECOT=2.3.18
|
ARG DOVECOT=2.3.19.1
|
||||||
ENV LC_ALL C
|
ENV LC_ALL C
|
||||||
ENV GOSU_VERSION 1.14
|
ENV GOSU_VERSION 1.14
|
||||||
|
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
||||||
cat <<EOF > /etc/dovecot/sogo_trusted_ip.conf
|
cat <<EOF > /etc/dovecot/sogo_trusted_ip.conf
|
||||||
# Autogenerated by mailcow
|
# Autogenerated by mailcow
|
||||||
remote ${IPV4_NETWORK}.248 {
|
remote ${IPV4_NETWORK}.248 {
|
||||||
@@ -349,6 +350,14 @@ sievec /var/vmail/sieve/global_sieve_after.sieve
|
|||||||
sievec /usr/lib/dovecot/sieve/report-spam.sieve
|
sievec /usr/lib/dovecot/sieve/report-spam.sieve
|
||||||
sievec /usr/lib/dovecot/sieve/report-ham.sieve
|
sievec /usr/lib/dovecot/sieve/report-ham.sieve
|
||||||
|
|
||||||
|
for file in /var/vmail/*/*/sieve/*.sieve ; do
|
||||||
|
if [[ "$file" == "/var/vmail/*/*/sieve/*.sieve" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
sievec "$file" "$(dirname "$file")/../.dovecot.svbin"
|
||||||
|
chown vmail:vmail "$(dirname "$file")/../.dovecot.svbin"
|
||||||
|
done
|
||||||
|
|
||||||
# Fix permissions
|
# Fix permissions
|
||||||
chown root:root /etc/dovecot/sql/*.conf
|
chown root:root /etc/dovecot/sql/*.conf
|
||||||
chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/lua/passwd-verify.lua
|
chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/lua/passwd-verify.lua
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ sub sig_handler {
|
|||||||
die "sig_handler received signal, preparing to exit...\n";
|
die "sig_handler received signal, preparing to exit...\n";
|
||||||
};
|
};
|
||||||
|
|
||||||
open my $file, '<', "/etc/sogo/sieve.creds";
|
open my $file, '<', "/etc/sogo/sieve.creds";
|
||||||
my $creds = <$file>;
|
my $creds = <$file>;
|
||||||
close $file;
|
close $file;
|
||||||
my ($master_user, $master_pass) = split /:/, $creds;
|
my ($master_user, $master_pass) = split /:/, $creds;
|
||||||
my $sth = $dbh->prepare("SELECT id,
|
my $sth = $dbh->prepare("SELECT id,
|
||||||
@@ -166,17 +166,11 @@ while ($row = $sth->fetchrow_arrayref()) {
|
|||||||
$success = 1;
|
$success = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
$keep_job_active = 1;
|
$update = $dbh->prepare("UPDATE imapsync SET returned_text = ?, success = ?, exit_status = ? WHERE id = ?");
|
||||||
if (defined $exit_status && $exit_status eq "EXIT_AUTHENTICATION_FAILURE_USER1") {
|
|
||||||
$keep_job_active = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$update = $dbh->prepare("UPDATE imapsync SET returned_text = ?, success = ?, exit_status = ?, active = ? WHERE id = ?");
|
|
||||||
$update->bind_param( 1, ${stdout} );
|
$update->bind_param( 1, ${stdout} );
|
||||||
$update->bind_param( 2, ${success} );
|
$update->bind_param( 2, ${success} );
|
||||||
$update->bind_param( 3, ${exit_status} );
|
$update->bind_param( 3, ${exit_status} );
|
||||||
$update->bind_param( 4, ${keep_job_active} );
|
$update->bind_param( 4, ${id} );
|
||||||
$update->bind_param( 5, ${id} );
|
|
||||||
$update->execute();
|
$update->execute();
|
||||||
} catch {
|
} catch {
|
||||||
$update = $dbh->prepare("UPDATE imapsync SET returned_text = 'Could not start or finish imapsync', success = 0 WHERE id = ?");
|
$update = $dbh->prepare("UPDATE imapsync SET returned_text = 'Could not start or finish imapsync', success = 0 WHERE id = ?");
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ try:
|
|||||||
def query_mysql(query, headers = True, update = False):
|
def query_mysql(query, headers = True, update = False):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
cnx = mysql.connector.connect(unix_socket = '/var/run/mysqld/mysqld.sock', user=os.environ.get('DBUSER'), passwd=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8")
|
cnx = mysql.connector.connect(unix_socket = '/var/run/mysqld/mysqld.sock', user=os.environ.get('DBUSER'), passwd=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8mb4", collation="utf8mb4_general_ci")
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print('%s - trying again...' % (ex))
|
print('%s - trying again...' % (ex))
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
@@ -166,4 +166,4 @@ try:
|
|||||||
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'], attrs['quarantine_category'])
|
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'], attrs['quarantine_category'])
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
os.unlink(pidfile)
|
os.unlink(pidfile)
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ def permBan(net, unban=False):
|
|||||||
if rule not in chain.rules and not unban:
|
if rule not in chain.rules and not unban:
|
||||||
logCrit('Add host/network %s to blacklist' % net)
|
logCrit('Add host/network %s to blacklist' % net)
|
||||||
chain.insert_rule(rule)
|
chain.insert_rule(rule)
|
||||||
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
||||||
elif rule in chain.rules and unban:
|
elif rule in chain.rules and unban:
|
||||||
logCrit('Remove host/network %s from blacklist' % net)
|
logCrit('Remove host/network %s from blacklist' % net)
|
||||||
chain.delete_rule(rule)
|
chain.delete_rule(rule)
|
||||||
@@ -267,7 +267,7 @@ def permBan(net, unban=False):
|
|||||||
if rule not in chain.rules and not unban:
|
if rule not in chain.rules and not unban:
|
||||||
logCrit('Add host/network %s to blacklist' % net)
|
logCrit('Add host/network %s to blacklist' % net)
|
||||||
chain.insert_rule(rule)
|
chain.insert_rule(rule)
|
||||||
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
||||||
elif rule in chain.rules and unban:
|
elif rule in chain.rules and unban:
|
||||||
logCrit('Remove host/network %s from blacklist' % net)
|
logCrit('Remove host/network %s from blacklist' % net)
|
||||||
chain.delete_rule(rule)
|
chain.delete_rule(rule)
|
||||||
@@ -346,6 +346,8 @@ def snat4(snat_target):
|
|||||||
rule.dst = '!' + rule.src
|
rule.dst = '!' + rule.src
|
||||||
target = rule.create_target("SNAT")
|
target = rule.create_target("SNAT")
|
||||||
target.to_source = snat_target
|
target.to_source = snat_target
|
||||||
|
match = rule.create_match("comment")
|
||||||
|
match.comment = f'{int(round(time.time()))}'
|
||||||
return rule
|
return rule
|
||||||
|
|
||||||
while not quit_now:
|
while not quit_now:
|
||||||
@@ -356,19 +358,26 @@ def snat4(snat_target):
|
|||||||
table.refresh()
|
table.refresh()
|
||||||
chain = iptc.Chain(table, 'POSTROUTING')
|
chain = iptc.Chain(table, 'POSTROUTING')
|
||||||
table.autocommit = False
|
table.autocommit = False
|
||||||
if get_snat4_rule() not in chain.rules:
|
new_rule = get_snat4_rule()
|
||||||
logCrit('Added POSTROUTING rule for source network %s to SNAT target %s' % (get_snat4_rule().src, snat_target))
|
for position, rule in enumerate(chain.rules):
|
||||||
chain.insert_rule(get_snat4_rule())
|
match = all((
|
||||||
table.commit()
|
new_rule.get_src() == rule.get_src(),
|
||||||
else:
|
new_rule.get_dst() == rule.get_dst(),
|
||||||
for position, item in enumerate(chain.rules):
|
new_rule.target.parameters == rule.target.parameters,
|
||||||
if item == get_snat4_rule():
|
new_rule.target.name == rule.target.name
|
||||||
if position != 0:
|
))
|
||||||
chain.delete_rule(get_snat4_rule())
|
if position == 0:
|
||||||
table.commit()
|
if not match:
|
||||||
|
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
|
||||||
|
chain.insert_rule(new_rule)
|
||||||
|
else:
|
||||||
|
if match:
|
||||||
|
logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
|
||||||
|
chain.delete_rule(rule)
|
||||||
|
table.commit()
|
||||||
table.autocommit = True
|
table.autocommit = True
|
||||||
except:
|
except:
|
||||||
print('Error running SNAT4, retrying...')
|
print('Error running SNAT4, retrying...')
|
||||||
|
|
||||||
def snat6(snat_target):
|
def snat6(snat_target):
|
||||||
global lock
|
global lock
|
||||||
@@ -402,7 +411,7 @@ def snat6(snat_target):
|
|||||||
table.commit()
|
table.commit()
|
||||||
table.autocommit = True
|
table.autocommit = True
|
||||||
except:
|
except:
|
||||||
print('Error running SNAT6, retrying...')
|
print('Error running SNAT6, retrying...')
|
||||||
|
|
||||||
def autopurge():
|
def autopurge():
|
||||||
while not quit_now:
|
while not quit_now:
|
||||||
@@ -468,7 +477,7 @@ def whitelistUpdate():
|
|||||||
if Counter(new_whitelist) != Counter(WHITELIST):
|
if Counter(new_whitelist) != Counter(WHITELIST):
|
||||||
WHITELIST = new_whitelist
|
WHITELIST = new_whitelist
|
||||||
logInfo('Whitelist was changed, it has %s entries' % len(WHITELIST))
|
logInfo('Whitelist 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():
|
||||||
global quit_now
|
global quit_now
|
||||||
@@ -479,7 +488,7 @@ def blacklistUpdate():
|
|||||||
new_blacklist = []
|
new_blacklist = []
|
||||||
if list:
|
if list:
|
||||||
new_blacklist = genNetworkList(list)
|
new_blacklist = genNetworkList(list)
|
||||||
if Counter(new_blacklist) != Counter(BLACKLIST):
|
if Counter(new_blacklist) != Counter(BLACKLIST):
|
||||||
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
|
||||||
@@ -490,7 +499,7 @@ def blacklistUpdate():
|
|||||||
if delban:
|
if delban:
|
||||||
for net in delban:
|
for net in delban:
|
||||||
permBan(net=net, unban=True)
|
permBan(net=net, unban=True)
|
||||||
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
||||||
|
|
||||||
def initChain():
|
def initChain():
|
||||||
# Is called before threads start, no locking
|
# Is called before threads start, no locking
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
FROM php:8.0-fpm-alpine3.16
|
FROM php:8.1-fpm-alpine3.16
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ENV APCU_PECL 5.1.21
|
ENV APCU_PECL 5.1.22
|
||||||
ENV IMAGICK_PECL 3.7.0
|
ENV IMAGICK_PECL 3.7.0
|
||||||
# Mailparse is pulled from master branch
|
ENV MAILPARSE_PECL 3.1.4
|
||||||
#ENV MAILPARSE_PECL 3.0.2
|
|
||||||
ENV MEMCACHED_PECL 3.2.0
|
ENV MEMCACHED_PECL 3.2.0
|
||||||
ENV REDIS_PECL 5.3.7
|
ENV REDIS_PECL 5.3.7
|
||||||
|
ENV COMPOSER 2.4.4
|
||||||
|
|
||||||
RUN apk add -U --no-cache autoconf \
|
RUN apk add -U --no-cache autoconf \
|
||||||
aspell-dev \
|
aspell-dev \
|
||||||
@@ -18,6 +18,7 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
freetype-dev \
|
freetype-dev \
|
||||||
g++ \
|
g++ \
|
||||||
git \
|
git \
|
||||||
|
gettext \
|
||||||
gettext-dev \
|
gettext-dev \
|
||||||
gmp-dev \
|
gmp-dev \
|
||||||
gnupg \
|
gnupg \
|
||||||
@@ -27,8 +28,11 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
imagemagick-dev \
|
imagemagick-dev \
|
||||||
imap-dev \
|
imap-dev \
|
||||||
jq \
|
jq \
|
||||||
|
libavif \
|
||||||
|
libavif-dev \
|
||||||
libjpeg-turbo \
|
libjpeg-turbo \
|
||||||
libjpeg-turbo-dev \
|
libjpeg-turbo-dev \
|
||||||
|
libmemcached \
|
||||||
libmemcached-dev \
|
libmemcached-dev \
|
||||||
libpng \
|
libpng \
|
||||||
libpng-dev \
|
libpng-dev \
|
||||||
@@ -38,7 +42,9 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
libtool \
|
libtool \
|
||||||
libwebp-dev \
|
libwebp-dev \
|
||||||
libxml2-dev \
|
libxml2-dev \
|
||||||
|
libxpm \
|
||||||
libxpm-dev \
|
libxpm-dev \
|
||||||
|
libzip \
|
||||||
libzip-dev \
|
libzip-dev \
|
||||||
make \
|
make \
|
||||||
mysql-client \
|
mysql-client \
|
||||||
@@ -49,22 +55,24 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
samba-client \
|
samba-client \
|
||||||
zlib-dev \
|
zlib-dev \
|
||||||
tzdata \
|
tzdata \
|
||||||
&& git clone https://github.com/php/pecl-mail-mailparse \
|
&& pecl install mailparse-${MAILPARSE_PECL} \
|
||||||
&& cd pecl-mail-mailparse \
|
&& pecl install redis-${REDIS_PECL} \
|
||||||
&& pecl install package.xml \
|
&& pecl install memcached-${MEMCACHED_PECL} \
|
||||||
&& cd .. \
|
&& pecl install APCu-${APCU_PECL} \
|
||||||
&& rm -r pecl-mail-mailparse \
|
&& pecl install imagick-${IMAGICK_PECL} \
|
||||||
&& pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} \
|
|
||||||
&& docker-php-ext-enable apcu imagick memcached mailparse redis \
|
&& docker-php-ext-enable apcu imagick memcached mailparse redis \
|
||||||
&& pecl clear-cache \
|
&& pecl clear-cache \
|
||||||
&& docker-php-ext-configure intl \
|
&& docker-php-ext-configure intl \
|
||||||
&& docker-php-ext-configure exif \
|
&& docker-php-ext-configure exif \
|
||||||
&& docker-php-ext-configure gd --with-freetype=/usr/include/ \
|
&& docker-php-ext-configure gd --with-freetype=/usr/include/ \
|
||||||
--with-jpeg=/usr/include/ \
|
--with-jpeg=/usr/include/ \
|
||||||
|
--with-webp \
|
||||||
|
--with-xpm \
|
||||||
|
--with-avif \
|
||||||
&& docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets zip bcmath gmp \
|
&& docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets zip bcmath gmp \
|
||||||
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \
|
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \
|
||||||
&& docker-php-ext-install -j 4 imap \
|
&& docker-php-ext-install -j 4 imap \
|
||||||
&& curl --silent --show-error https://getcomposer.org/installer | php \
|
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER} \
|
||||||
&& mv composer.phar /usr/local/bin/composer \
|
&& mv composer.phar /usr/local/bin/composer \
|
||||||
&& chmod +x /usr/local/bin/composer \
|
&& chmod +x /usr/local/bin/composer \
|
||||||
&& apk del --purge autoconf \
|
&& apk del --purge autoconf \
|
||||||
@@ -72,15 +80,21 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
cyrus-sasl-dev \
|
cyrus-sasl-dev \
|
||||||
freetype-dev \
|
freetype-dev \
|
||||||
g++ \
|
g++ \
|
||||||
|
gettext-dev \
|
||||||
icu-dev \
|
icu-dev \
|
||||||
imagemagick-dev \
|
imagemagick-dev \
|
||||||
imap-dev \
|
imap-dev \
|
||||||
|
libavif-dev \
|
||||||
libjpeg-turbo-dev \
|
libjpeg-turbo-dev \
|
||||||
|
libmemcached-dev \
|
||||||
libpng-dev \
|
libpng-dev \
|
||||||
libressl-dev \
|
libressl-dev \
|
||||||
libwebp-dev \
|
libwebp-dev \
|
||||||
libxml2-dev \
|
libxml2-dev \
|
||||||
|
libxpm-dev \
|
||||||
|
libzip-dev \
|
||||||
make \
|
make \
|
||||||
|
openldap-dev \
|
||||||
pcre-dev \
|
pcre-dev \
|
||||||
zlib-dev
|
zlib-dev
|
||||||
|
|
||||||
|
|||||||
@@ -323,7 +323,19 @@ hosts = unix:/var/run/mysqld/mysqld.sock
|
|||||||
dbname = ${DBNAME}
|
dbname = ${DBNAME}
|
||||||
# First select queries domain and alias_domain to determine if domains are active.
|
# First select queries domain and alias_domain to determine if domains are active.
|
||||||
query = SELECT goto FROM alias
|
query = SELECT goto FROM alias
|
||||||
WHERE address='%s'
|
WHERE id IN (
|
||||||
|
SELECT COALESCE (
|
||||||
|
(
|
||||||
|
SELECT id FROM alias
|
||||||
|
WHERE address='%s'
|
||||||
|
AND (active='1' OR active='2')
|
||||||
|
), (
|
||||||
|
SELECT id FROM alias
|
||||||
|
WHERE address='@%d'
|
||||||
|
AND (active='1' OR active='2')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
AND active='1'
|
AND active='1'
|
||||||
AND (domain IN
|
AND (domain IN
|
||||||
(SELECT domain FROM domain
|
(SELECT domain FROM domain
|
||||||
@@ -354,7 +366,7 @@ query = SELECT goto FROM alias
|
|||||||
WHERE alias_domain.alias_domain = '%d'
|
WHERE alias_domain.alias_domain = '%d'
|
||||||
AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain)
|
AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain)
|
||||||
AND (mailbox.active = '1' OR mailbox.active ='2')
|
AND (mailbox.active = '1' OR mailbox.active ='2')
|
||||||
AND alias_domain.active='1'
|
AND alias_domain.active='1';
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# MX based routing
|
# MX based routing
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ FROM debian:bullseye-slim
|
|||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ARG SOGO_DEBIAN_REPOSITORY=http://packages.inverse.ca/SOGo/nightly/5/debian/
|
ARG SOGO_DEBIAN_REPOSITORY=http://packages.sogo.nu/nightly/5/debian/
|
||||||
ENV LC_ALL C
|
ENV LC_ALL C
|
||||||
ENV GOSU_VERSION 1.14
|
ENV GOSU_VERSION 1.14
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
|
|||||||
&& gosu nobody true \
|
&& gosu nobody true \
|
||||||
&& mkdir /usr/share/doc/sogo \
|
&& mkdir /usr/share/doc/sogo \
|
||||||
&& touch /usr/share/doc/sogo/empty.sh \
|
&& touch /usr/share/doc/sogo/empty.sh \
|
||||||
&& apt-key adv --keyserver keyserver.ubuntu.com --recv-key 0x810273C4 \
|
&& apt-key adv --keyserver keys.openpgp.org --recv-key 74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9 \
|
||||||
&& echo "deb ${SOGO_DEBIAN_REPOSITORY} bullseye bullseye" > /etc/apt/sources.list.d/sogo.list \
|
&& echo "deb ${SOGO_DEBIAN_REPOSITORY} bullseye bullseye" > /etc/apt/sources.list.d/sogo.list \
|
||||||
&& apt-get update && apt-get install -y --no-install-recommends \
|
&& apt-get update && apt-get install -y --no-install-recommends \
|
||||||
sogo \
|
sogo \
|
||||||
@@ -52,4 +52,4 @@ RUN chmod +x /bootstrap-sogo.sh \
|
|||||||
|
|
||||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
|
|
||||||
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||||
@@ -142,6 +142,10 @@ cat <<EOF > /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
|||||||
<string>mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_acl</string>
|
<string>mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_acl</string>
|
||||||
<key>SOGoIMAPServer</key>
|
<key>SOGoIMAPServer</key>
|
||||||
<string>imap://${IPV4_NETWORK}.250:143/?TLS=YES&tlsVerifyMode=none</string>
|
<string>imap://${IPV4_NETWORK}.250:143/?TLS=YES&tlsVerifyMode=none</string>
|
||||||
|
<key>SOGoSieveServer</key>
|
||||||
|
<string>sieve://${IPV4_NETWORK}.250:4190/?TLS=YES&tlsVerifyMode=none</string>
|
||||||
|
<key>SOGoSMTPServer</key>
|
||||||
|
<string>smtp://${IPV4_NETWORK}.253:588/?TLS=YES&tlsVerifyMode=none</string>
|
||||||
<key>SOGoTrustProxyAuthentication</key>
|
<key>SOGoTrustProxyAuthentication</key>
|
||||||
<string>YES</string>
|
<string>YES</string>
|
||||||
<key>SOGoEncryptionKey</key>
|
<key>SOGoEncryptionKey</key>
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ symbols {
|
|||||||
"ENCRYPTED_CHAT" {
|
"ENCRYPTED_CHAT" {
|
||||||
score = -20.0;
|
score = -20.0;
|
||||||
}
|
}
|
||||||
|
"SOGO_CONTACT" {
|
||||||
|
score = -99.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group "MX" {
|
group "MX" {
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ rules {
|
|||||||
backend = "http";
|
backend = "http";
|
||||||
url = "http://nginx:9081/pushover.php";
|
url = "http://nginx:9081/pushover.php";
|
||||||
selector = "mailcow_rcpt";
|
selector = "mailcow_rcpt";
|
||||||
# Only return msgid, do not parse the full message
|
formatter = "json";
|
||||||
formatter = "msgid";
|
|
||||||
meta_headers = true;
|
meta_headers = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ if (!function_exists('getallheaders')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$headers = getallheaders();
|
$headers = getallheaders();
|
||||||
|
$json_body = json_decode(file_get_contents('php://input'));
|
||||||
|
|
||||||
$qid = $headers['X-Rspamd-Qid'];
|
$qid = $headers['X-Rspamd-Qid'];
|
||||||
$rcpts = $headers['X-Rspamd-Rcpt'];
|
$rcpts = $headers['X-Rspamd-Rcpt'];
|
||||||
@@ -65,6 +66,20 @@ if (is_array($symbols_array)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sender_address = $json_body->header_from[0];
|
||||||
|
$sender_name = '-';
|
||||||
|
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $sender_address, $matches)) {
|
||||||
|
$sender_address = $matches['address'];
|
||||||
|
$sender_name = trim($matches['name'], '"\' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
$to_address = $json_body->header_to[0];
|
||||||
|
$to_name = '-';
|
||||||
|
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $to_address, $matches)) {
|
||||||
|
$to_address = $matches['address'];
|
||||||
|
$to_name = trim($matches['name'], '"\' ');
|
||||||
|
}
|
||||||
|
|
||||||
$rcpt_final_mailboxes = array();
|
$rcpt_final_mailboxes = array();
|
||||||
|
|
||||||
// Loop through all rcpts
|
// Loop through all rcpts
|
||||||
@@ -229,9 +244,16 @@ foreach ($rcpt_final_mailboxes as $rcpt_final) {
|
|||||||
$post_fields = array(
|
$post_fields = array(
|
||||||
"token" => $api_data['token'],
|
"token" => $api_data['token'],
|
||||||
"user" => $api_data['key'],
|
"user" => $api_data['key'],
|
||||||
"title" => sprintf("%s", str_replace(array('{SUBJECT}', '{SENDER}'), array($subject, $sender), $title)),
|
"title" => sprintf("%s", str_replace(
|
||||||
|
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}'),
|
||||||
|
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address), $title)
|
||||||
|
),
|
||||||
"priority" => $priority,
|
"priority" => $priority,
|
||||||
"message" => sprintf("%s", str_replace(array('{SUBJECT}', '{SENDER}'), array($subject, $sender), $text))
|
"message" => sprintf("%s", str_replace(
|
||||||
|
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}', '\n'),
|
||||||
|
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address, PHP_EOL), $text)
|
||||||
|
),
|
||||||
|
"sound" => $attributes['sound'] ?? "pushover"
|
||||||
);
|
);
|
||||||
if ($attributes['evaluate_x_prio'] == "1" && $priority == 1) {
|
if ($attributes['evaluate_x_prio'] == "1" && $priority == 1) {
|
||||||
$post_fields['expire'] = 600;
|
$post_fields['expire'] = 600;
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
// );
|
// );
|
||||||
|
|
||||||
// self-signed is not trusted anymore
|
// self-signed is not trusted anymore
|
||||||
SOGoSieveServer = "sieve://dovecot:4190/?TLS=YES&tlsVerifyMode=none";
|
|
||||||
SOGoSMTPServer = "smtp://postfix:588/?TLS=YES&tlsVerifyMode=none";
|
|
||||||
WOPort = "0.0.0.0:20000";
|
WOPort = "0.0.0.0:20000";
|
||||||
SOGoMemcachedHost = "memcached";
|
SOGoMemcachedHost = "memcached";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: -moz-scrollbars-vertical;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
+2
-43
@@ -5,56 +5,15 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Swagger UI</title>
|
<title>Swagger UI</title>
|
||||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="index.css" />
|
||||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||||
<style>
|
|
||||||
html
|
|
||||||
{
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: -moz-scrollbars-vertical;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
*,
|
|
||||||
*:before,
|
|
||||||
*:after
|
|
||||||
{
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
body
|
|
||||||
{
|
|
||||||
margin:0;
|
|
||||||
background: #fafafa;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="swagger-ui"></div>
|
<div id="swagger-ui"></div>
|
||||||
|
|
||||||
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
||||||
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
||||||
<script>
|
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
|
||||||
window.onload = function() {
|
|
||||||
// Begin Swagger UI call region
|
|
||||||
const ui = SwaggerUIBundle({
|
|
||||||
urls: [{url: "/api/openapi.yaml", name: "mailcow API"}],
|
|
||||||
dom_id: '#swagger-ui',
|
|
||||||
deepLinking: true,
|
|
||||||
presets: [
|
|
||||||
SwaggerUIBundle.presets.apis,
|
|
||||||
SwaggerUIStandalonePreset
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
SwaggerUIBundle.plugins.DownloadUrl
|
|
||||||
],
|
|
||||||
layout: "StandaloneLayout"
|
|
||||||
});
|
|
||||||
// End Swagger UI call region
|
|
||||||
|
|
||||||
window.ui = ui;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
var isValid, qp, arr;
|
var isValid, qp, arr;
|
||||||
|
|
||||||
if (/code|token|error/.test(window.location.hash)) {
|
if (/code|token|error/.test(window.location.hash)) {
|
||||||
qp = window.location.hash.substring(1);
|
qp = window.location.hash.substring(1).replace('?', '&');
|
||||||
} else {
|
} else {
|
||||||
qp = location.search.substring(1);
|
qp = location.search.substring(1);
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
authId: oauth2.auth.name,
|
authId: oauth2.auth.name,
|
||||||
source: "auth",
|
source: "auth",
|
||||||
level: "warning",
|
level: "warning",
|
||||||
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
|
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
authId: oauth2.auth.name,
|
authId: oauth2.auth.name,
|
||||||
source: "auth",
|
source: "auth",
|
||||||
level: "error",
|
level: "error",
|
||||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -67,9 +67,13 @@
|
|||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', function () {
|
if (document.readyState !== 'loading') {
|
||||||
run();
|
run();
|
||||||
});
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
run();
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+154
-51
@@ -518,21 +518,23 @@ paths:
|
|||||||
- domain.tld
|
- domain.tld
|
||||||
type: success
|
type: success
|
||||||
schema:
|
schema:
|
||||||
properties:
|
type: array
|
||||||
log:
|
items:
|
||||||
description: contains request object
|
type: object
|
||||||
items: {}
|
properties:
|
||||||
type: array
|
log:
|
||||||
msg:
|
description: contains request object
|
||||||
items: {}
|
items: {}
|
||||||
type: array
|
type: array
|
||||||
type:
|
msg:
|
||||||
enum:
|
items: {}
|
||||||
- success
|
type: array
|
||||||
- danger
|
type:
|
||||||
- error
|
enum:
|
||||||
type: string
|
- success
|
||||||
type: object
|
- danger
|
||||||
|
- error
|
||||||
|
type: string
|
||||||
description: OK
|
description: OK
|
||||||
headers: {}
|
headers: {}
|
||||||
tags:
|
tags:
|
||||||
@@ -579,6 +581,11 @@ paths:
|
|||||||
domain:
|
domain:
|
||||||
description: Fully qualified domain name
|
description: Fully qualified domain name
|
||||||
type: string
|
type: string
|
||||||
|
gal:
|
||||||
|
description: >-
|
||||||
|
is domain global address list active or not, it enables
|
||||||
|
shared contacts accross domain in SOGo webmail
|
||||||
|
type: boolean
|
||||||
mailboxes:
|
mailboxes:
|
||||||
description: limit count of mailboxes associated with this domain
|
description: limit count of mailboxes associated with this domain
|
||||||
type: number
|
type: number
|
||||||
@@ -596,6 +603,9 @@ paths:
|
|||||||
if not, them you have to create "dummy" mailbox for each
|
if not, them you have to create "dummy" mailbox for each
|
||||||
address to relay
|
address to relay
|
||||||
type: boolean
|
type: boolean
|
||||||
|
relay_unknown_only:
|
||||||
|
description: Relay non-existing mailboxes only. Existing mailboxes will be delivered locally.
|
||||||
|
type: boolean
|
||||||
rl_frame:
|
rl_frame:
|
||||||
enum:
|
enum:
|
||||||
- s
|
- s
|
||||||
@@ -606,6 +616,11 @@ paths:
|
|||||||
rl_value:
|
rl_value:
|
||||||
description: rate limit value
|
description: rate limit value
|
||||||
type: number
|
type: number
|
||||||
|
tags:
|
||||||
|
description: tags for this Domain
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
summary: Create domain
|
summary: Create domain
|
||||||
/api/v1/add/domain-admin:
|
/api/v1/add/domain-admin:
|
||||||
@@ -1952,21 +1967,23 @@ paths:
|
|||||||
- domain2.tld
|
- domain2.tld
|
||||||
type: success
|
type: success
|
||||||
schema:
|
schema:
|
||||||
properties:
|
type: array
|
||||||
log:
|
items:
|
||||||
description: contains request object
|
type: object
|
||||||
items: {}
|
properties:
|
||||||
type: array
|
log:
|
||||||
msg:
|
description: contains request object
|
||||||
items: {}
|
items: {}
|
||||||
type: array
|
type: array
|
||||||
type:
|
msg:
|
||||||
enum:
|
items: {}
|
||||||
- success
|
type: array
|
||||||
- danger
|
type:
|
||||||
- error
|
enum:
|
||||||
type: string
|
- success
|
||||||
type: object
|
- danger
|
||||||
|
- error
|
||||||
|
type: string
|
||||||
description: OK
|
description: OK
|
||||||
headers: {}
|
headers: {}
|
||||||
tags:
|
tags:
|
||||||
@@ -1977,14 +1994,15 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
|
type: object
|
||||||
example:
|
example:
|
||||||
- domain.tld
|
- domain.tld
|
||||||
- domain2.tld
|
- domain2.tld
|
||||||
properties:
|
properties:
|
||||||
items:
|
items:
|
||||||
description: contains list of domains you want to delete
|
type: array
|
||||||
type: object
|
items:
|
||||||
type: object
|
type: string
|
||||||
summary: Delete domain
|
summary: Delete domain
|
||||||
/api/v1/delete/domain-admin:
|
/api/v1/delete/domain-admin:
|
||||||
post:
|
post:
|
||||||
@@ -2972,23 +2990,25 @@ paths:
|
|||||||
$ref: "#/components/responses/Unauthorized"
|
$ref: "#/components/responses/Unauthorized"
|
||||||
"200":
|
"200":
|
||||||
content:
|
content:
|
||||||
"*/*":
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
properties:
|
type: array
|
||||||
log:
|
items:
|
||||||
description: contains request object
|
type: object
|
||||||
items: {}
|
properties:
|
||||||
type: array
|
log:
|
||||||
msg:
|
type: array
|
||||||
items: {}
|
description: contains request object
|
||||||
type: array
|
items: {}
|
||||||
type:
|
msg:
|
||||||
enum:
|
type: array
|
||||||
- success
|
items: {}
|
||||||
- danger
|
type:
|
||||||
- error
|
enum:
|
||||||
type: string
|
- success
|
||||||
type: object
|
- danger
|
||||||
|
- error
|
||||||
|
type: string
|
||||||
description: OK
|
description: OK
|
||||||
headers: {}
|
headers: {}
|
||||||
tags:
|
tags:
|
||||||
@@ -3056,13 +3076,33 @@ paths:
|
|||||||
if not, them you have to create "dummy" mailbox for each
|
if not, them you have to create "dummy" mailbox for each
|
||||||
address to relay
|
address to relay
|
||||||
type: boolean
|
type: boolean
|
||||||
|
relay_unknown_only:
|
||||||
|
description: Relay non-existing mailboxes only. Existing mailboxes will be delivered locally.
|
||||||
|
type: boolean
|
||||||
relayhost:
|
relayhost:
|
||||||
description: id of relayhost
|
description: id of relayhost
|
||||||
type: number
|
type: number
|
||||||
|
rl_frame:
|
||||||
|
enum:
|
||||||
|
- s
|
||||||
|
- m
|
||||||
|
- h
|
||||||
|
- d
|
||||||
|
type: string
|
||||||
|
rl_value:
|
||||||
|
description: rate limit value
|
||||||
|
type: number
|
||||||
|
tags:
|
||||||
|
description: tags for this Domain
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
items:
|
items:
|
||||||
description: contains list of domain names you want update
|
description: contains list of domain names you want update
|
||||||
type: object
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
summary: Update domain
|
summary: Update domain
|
||||||
/api/v1/edit/fail2ban:
|
/api/v1/edit/fail2ban:
|
||||||
@@ -3309,6 +3349,7 @@ paths:
|
|||||||
evaluate_x_prio: "0"
|
evaluate_x_prio: "0"
|
||||||
key: 21e8918e1jksdjcpis712
|
key: 21e8918e1jksdjcpis712
|
||||||
only_x_prio: "0"
|
only_x_prio: "0"
|
||||||
|
sound: "pushover"
|
||||||
senders: ""
|
senders: ""
|
||||||
senders_regex: ""
|
senders_regex: ""
|
||||||
text: ""
|
text: ""
|
||||||
@@ -3352,6 +3393,7 @@ paths:
|
|||||||
evaluate_x_prio: "0"
|
evaluate_x_prio: "0"
|
||||||
key: 21e8918e1jksdjcpis712
|
key: 21e8918e1jksdjcpis712
|
||||||
only_x_prio: "0"
|
only_x_prio: "0"
|
||||||
|
sound: "pushover"
|
||||||
senders: ""
|
senders: ""
|
||||||
senders_regex: ""
|
senders_regex: ""
|
||||||
text: ""
|
text: ""
|
||||||
@@ -3373,6 +3415,9 @@ paths:
|
|||||||
only_x_prio:
|
only_x_prio:
|
||||||
description: Only send push for prio mails
|
description: Only send push for prio mails
|
||||||
type: number
|
type: number
|
||||||
|
sound:
|
||||||
|
description: Set notification sound
|
||||||
|
type: string
|
||||||
senders:
|
senders:
|
||||||
description: Only send push for emails from these senders
|
description: Only send push for emails from these senders
|
||||||
type: string
|
type: string
|
||||||
@@ -3953,6 +3998,8 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: tags
|
name: tags
|
||||||
required: false
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- description: e.g. api-key-string
|
- description: e.g. api-key-string
|
||||||
example: api-key-string
|
example: api-key-string
|
||||||
in: header
|
in: header
|
||||||
@@ -4512,6 +4559,8 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: tags
|
name: tags
|
||||||
required: false
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- description: e.g. api-key-string
|
- description: e.g. api-key-string
|
||||||
example: api-key-string
|
example: api-key-string
|
||||||
in: header
|
in: header
|
||||||
@@ -5457,6 +5506,60 @@ paths:
|
|||||||
attr:
|
attr:
|
||||||
spam_score: "8,15"
|
spam_score: "8,15"
|
||||||
summary: Edit mailbox spam filter score
|
summary: Edit mailbox spam filter score
|
||||||
|
"/api/v1/get/mailbox/all/{domain}":
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: name of domain
|
||||||
|
in: path
|
||||||
|
name: domain
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: e.g. api-key-string
|
||||||
|
example: api-key-string
|
||||||
|
in: header
|
||||||
|
name: X-API-Key
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"401":
|
||||||
|
$ref: "#/components/responses/Unauthorized"
|
||||||
|
"200":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
examples:
|
||||||
|
response:
|
||||||
|
value:
|
||||||
|
- active: "1"
|
||||||
|
attributes:
|
||||||
|
force_pw_update: "0"
|
||||||
|
mailbox_format: "maildir:"
|
||||||
|
quarantine_notification: never
|
||||||
|
sogo_access: "1"
|
||||||
|
tls_enforce_in: "0"
|
||||||
|
tls_enforce_out: "0"
|
||||||
|
domain: domain3.tld
|
||||||
|
is_relayed: 0
|
||||||
|
local_part: info
|
||||||
|
max_new_quota: 10737418240
|
||||||
|
messages: 0
|
||||||
|
name: Full name
|
||||||
|
percent_class: success
|
||||||
|
percent_in_use: 0
|
||||||
|
quota: 3221225472
|
||||||
|
quota_used: 0
|
||||||
|
rl: false
|
||||||
|
spam_aliases: 0
|
||||||
|
username: info@domain3.tld
|
||||||
|
tags: ["tag1", "tag2"]
|
||||||
|
description: OK
|
||||||
|
headers: {}
|
||||||
|
tags:
|
||||||
|
- Mailboxes
|
||||||
|
description: You can list all mailboxes existing in system for a specific domain.
|
||||||
|
operationId: Get mailboxes of a domain
|
||||||
|
summary: Get mailboxes of a domain
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
- name: Domains
|
- name: Domains
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
window.onload = function() {
|
||||||
|
// Begin Swagger UI call region
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
urls: [{url: "/api/openapi.yaml", name: "mailcow API"}],
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
deepLinking: true,
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
SwaggerUIBundle.plugins.DownloadUrl
|
||||||
|
],
|
||||||
|
layout: "StandaloneLayout"
|
||||||
|
});
|
||||||
|
// End Swagger UI call region
|
||||||
|
|
||||||
|
window.ui = ui;
|
||||||
|
};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
@@ -128,10 +128,10 @@ table.footable > tbody > tr.footable-empty > th {
|
|||||||
content: "\f130";
|
content: "\f130";
|
||||||
}
|
}
|
||||||
.fooicon-plus:before {
|
.fooicon-plus:before {
|
||||||
content: "\f4fc";
|
content: "\f4fd";
|
||||||
}
|
}
|
||||||
.fooicon-minus:before {
|
.fooicon-minus:before {
|
||||||
content: "\f2e8";
|
content: "\f2e9";
|
||||||
}
|
}
|
||||||
.fooicon-search:before {
|
.fooicon-search:before {
|
||||||
content: "\f52a";
|
content: "\f52a";
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -260,6 +260,18 @@ code {
|
|||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-group-item.webauthn-authenticator-selection,
|
||||||
|
.list-group-item.totp-authenticator-selection,
|
||||||
|
.list-group-item.yubi_otp-authenticator-selection {
|
||||||
|
border-radius: 0px !important;
|
||||||
|
}
|
||||||
|
.pending-tfa-collapse {
|
||||||
|
padding: 10px;
|
||||||
|
background: #fbfbfb;
|
||||||
|
border: 1px solid #ededed;
|
||||||
|
min-height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
.tag-box {
|
.tag-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -296,4 +308,3 @@ code {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
session_start();
|
session_start();
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ elseif (!empty($_GET['id']) && ctype_alnum($_GET['id'])) {
|
|||||||
$data['fuzzy_hashes'] = json_decode($mailc['fuzzy_hashes']);
|
$data['fuzzy_hashes'] = json_decode($mailc['fuzzy_hashes']);
|
||||||
// Get text/plain content
|
// Get text/plain content
|
||||||
$data['text_plain'] = $mail_parser->getMessageBody('text');
|
$data['text_plain'] = $mail_parser->getMessageBody('text');
|
||||||
|
if (!json_encode($data['text_plain'])) $data['text_plain'] = '';
|
||||||
// Get html content and convert to text
|
// Get html content and convert to text
|
||||||
$data['text_html'] = $html2text->convert($mail_parser->getMessageBody('html'));
|
$data['text_html'] = $html2text->convert($mail_parser->getMessageBody('html'));
|
||||||
if (empty($data['text_plain']) && empty($data['text_html'])) {
|
if (empty($data['text_plain']) && empty($data['text_html'])) {
|
||||||
|
|||||||
@@ -23,14 +23,43 @@ if (is_array($alertbox_log_parser)) {
|
|||||||
unset($_SESSION['return']);
|
unset($_SESSION['return']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// map tfa details for twig
|
||||||
|
$pending_tfa_authmechs = [];
|
||||||
|
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'])
|
||||||
|
&& isset($pending_tfa_authmechs['yubi_otp'])) {
|
||||||
|
$pending_tfa_authmechs['yubi_otp'] = true;
|
||||||
|
}
|
||||||
|
if (!isset($pending_tfa_authmechs['webauthn'])
|
||||||
|
&& !isset($pending_tfa_authmechs['yubi_otp'])
|
||||||
|
&& isset($pending_tfa_authmechs['totp'])) {
|
||||||
|
$pending_tfa_authmechs['totp'] = true;
|
||||||
|
}
|
||||||
|
if (isset($pending_tfa_authmechs['u2f'])) {
|
||||||
|
$pending_tfa_authmechs['u2f'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
// globals
|
// globals
|
||||||
$globalVariables = [
|
$globalVariables = [
|
||||||
'mailcow_info' => array(
|
'mailcow_info' => array(
|
||||||
'version_tag' => $GLOBALS['MAILCOW_GIT_VERSION'],
|
'version_tag' => $GLOBALS['MAILCOW_GIT_VERSION'],
|
||||||
'git_project_url' => $GLOBALS['MAILCOW_GIT_URL']
|
'last_version_tag' => $GLOBALS['MAILCOW_LAST_GIT_VERSION'],
|
||||||
|
'git_owner' => $GLOBALS['MAILCOW_GIT_OWNER'],
|
||||||
|
'git_repo' => $GLOBALS['MAILCOW_GIT_REPO'],
|
||||||
|
'git_project_url' => $GLOBALS['MAILCOW_GIT_URL'],
|
||||||
|
'git_commit' => $GLOBALS['MAILCOW_GIT_COMMIT'],
|
||||||
|
'git_commit_date' => $GLOBALS['MAILCOW_GIT_COMMIT_DATE'],
|
||||||
|
'mailcow_branch' => $GLOBALS['MAILCOW_BRANCH'],
|
||||||
|
'updated_at' => $GLOBALS['MAILCOW_UPDATEDAT']
|
||||||
),
|
),
|
||||||
'js_path' => '/cache/'.basename($JSPath),
|
'js_path' => '/cache/'.basename($JSPath),
|
||||||
'pending_tfa_method' => @$_SESSION['pending_tfa_method'],
|
'pending_tfa_methods' => @$_SESSION['pending_tfa_methods'],
|
||||||
|
'pending_tfa_authmechs' => $pending_tfa_authmechs,
|
||||||
'pending_mailcow_cc_username' => @$_SESSION['pending_mailcow_cc_username'],
|
'pending_mailcow_cc_username' => @$_SESSION['pending_mailcow_cc_username'],
|
||||||
'lang_footer' => json_encode($lang['footer']),
|
'lang_footer' => json_encode($lang['footer']),
|
||||||
'lang_acl' => json_encode($lang['acl']),
|
'lang_acl' => json_encode($lang['acl']),
|
||||||
|
|||||||
+249
-179
@@ -830,11 +830,15 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
$stmt->execute(array(':user' => $user));
|
$stmt->execute(array(':user' => $user));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
|
// verify password
|
||||||
if (verify_hash($row['password'], $pass)) {
|
if (verify_hash($row['password'], $pass)) {
|
||||||
if (get_tfa($user)['name'] != "none") {
|
// check for tfa authenticators
|
||||||
|
$authenticators = get_tfa($user);
|
||||||
|
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) {
|
||||||
|
// active tfa authenticators found, set pending user login
|
||||||
$_SESSION['pending_mailcow_cc_username'] = $user;
|
$_SESSION['pending_mailcow_cc_username'] = $user;
|
||||||
$_SESSION['pending_mailcow_cc_role'] = "admin";
|
$_SESSION['pending_mailcow_cc_role'] = "admin";
|
||||||
$_SESSION['pending_tfa_method'] = get_tfa($user)['name'];
|
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
|
||||||
unset($_SESSION['ldelay']);
|
unset($_SESSION['ldelay']);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'info',
|
'type' => 'info',
|
||||||
@@ -842,8 +846,7 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
'msg' => 'awaiting_tfa_confirmation'
|
'msg' => 'awaiting_tfa_confirmation'
|
||||||
);
|
);
|
||||||
return "pending";
|
return "pending";
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
unset($_SESSION['ldelay']);
|
unset($_SESSION['ldelay']);
|
||||||
// Reactivate TFA if it was set to "deactivate TFA for next login"
|
// Reactivate TFA if it was set to "deactivate TFA for next login"
|
||||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
|
||||||
@@ -866,11 +869,14 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
$stmt->execute(array(':user' => $user));
|
$stmt->execute(array(':user' => $user));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
|
// verify password
|
||||||
if (verify_hash($row['password'], $pass) !== false) {
|
if (verify_hash($row['password'], $pass) !== false) {
|
||||||
if (get_tfa($user)['name'] != "none") {
|
// check for tfa authenticators
|
||||||
|
$authenticators = get_tfa($user);
|
||||||
|
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) {
|
||||||
$_SESSION['pending_mailcow_cc_username'] = $user;
|
$_SESSION['pending_mailcow_cc_username'] = $user;
|
||||||
$_SESSION['pending_mailcow_cc_role'] = "domainadmin";
|
$_SESSION['pending_mailcow_cc_role'] = "domainadmin";
|
||||||
$_SESSION['pending_tfa_method'] = get_tfa($user)['name'];
|
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
|
||||||
unset($_SESSION['ldelay']);
|
unset($_SESSION['ldelay']);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'info',
|
'type' => 'info',
|
||||||
@@ -929,15 +935,37 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
$stmt->execute(array(':user' => $user));
|
$stmt->execute(array(':user' => $user));
|
||||||
$rows = array_merge($rows, $stmt->fetchAll(PDO::FETCH_ASSOC));
|
$rows = array_merge($rows, $stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
}
|
}
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
|
// verify password
|
||||||
if (verify_hash($row['password'], $pass) !== false) {
|
if (verify_hash($row['password'], $pass) !== false) {
|
||||||
unset($_SESSION['ldelay']);
|
if (!array_key_exists("app_passwd_id", $row)){
|
||||||
$_SESSION['return'][] = array(
|
// password is not a app password
|
||||||
'type' => 'success',
|
// check for tfa authenticators
|
||||||
'log' => array(__FUNCTION__, $user, '*'),
|
$authenticators = get_tfa($user);
|
||||||
'msg' => array('logged_in_as', $user)
|
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 &&
|
||||||
);
|
$app_passwd_data['eas'] !== true && $app_passwd_data['dav'] !== true) {
|
||||||
if ($app_passwd_data['eas'] === true || $app_passwd_data['dav'] === true) {
|
// authenticators found, init TFA flow
|
||||||
|
$_SESSION['pending_mailcow_cc_username'] = $user;
|
||||||
|
$_SESSION['pending_mailcow_cc_role'] = "user";
|
||||||
|
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
|
||||||
|
unset($_SESSION['ldelay']);
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'log' => array(__FUNCTION__, $user, '*'),
|
||||||
|
'msg' => array('logged_in_as', $user)
|
||||||
|
);
|
||||||
|
return "pending";
|
||||||
|
} else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
|
||||||
|
// no authenticators found, login successfull
|
||||||
|
// Reactivate TFA if it was set to "deactivate TFA for next login"
|
||||||
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
|
||||||
|
$stmt->execute(array(':user' => $user));
|
||||||
|
|
||||||
|
unset($_SESSION['ldelay']);
|
||||||
|
return "user";
|
||||||
|
}
|
||||||
|
} elseif ($app_passwd_data['eas'] === true || $app_passwd_data['dav'] === true) {
|
||||||
|
// password is a app password
|
||||||
$service = ($app_passwd_data['eas'] === true) ? 'EAS' : 'DAV';
|
$service = ($app_passwd_data['eas'] === true) ? 'EAS' : 'DAV';
|
||||||
$stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
|
$stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
@@ -946,8 +974,10 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
|||||||
':username' => $user,
|
':username' => $user,
|
||||||
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
||||||
));
|
));
|
||||||
|
|
||||||
|
unset($_SESSION['ldelay']);
|
||||||
|
return "user";
|
||||||
}
|
}
|
||||||
return "user";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1142,47 +1172,46 @@ function set_tfa($_data) {
|
|||||||
global $yubi;
|
global $yubi;
|
||||||
global $tfa;
|
global $tfa;
|
||||||
$_data_log = $_data;
|
$_data_log = $_data;
|
||||||
|
$access_denied = null;
|
||||||
!isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*';
|
!isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*';
|
||||||
$username = $_SESSION['mailcow_cc_username'];
|
$username = $_SESSION['mailcow_cc_username'];
|
||||||
if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
|
|
||||||
$_SESSION['return'][] = array(
|
// check for empty user and role
|
||||||
'type' => 'danger',
|
if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) $access_denied = true;
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
|
||||||
'msg' => 'access_denied'
|
// check admin confirm password
|
||||||
);
|
if ($access_denied === null) {
|
||||||
return false;
|
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
|
||||||
}
|
WHERE `username` = :username");
|
||||||
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
|
$stmt->execute(array(':username' => $username));
|
||||||
WHERE `username` = :username");
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
$stmt->execute(array(':username' => $username));
|
if ($row) {
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true;
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
else $access_denied = false;
|
||||||
if (!empty($num_results)) {
|
|
||||||
if (!verify_hash($row['password'], $_data["confirm_password"])) {
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
|
||||||
'msg' => 'access_denied'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
|
|
||||||
WHERE `username` = :username");
|
|
||||||
$stmt->execute(array(':username' => $username));
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
|
||||||
if (!empty($num_results)) {
|
|
||||||
if (!verify_hash($row['password'], $_data["confirm_password"])) {
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
|
||||||
'msg' => 'access_denied'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check mailbox confirm password
|
||||||
|
if ($access_denied === null) {
|
||||||
|
$stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
|
||||||
|
WHERE `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($row) {
|
||||||
|
if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true;
|
||||||
|
else $access_denied = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set access_denied error
|
||||||
|
if ($access_denied){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_data_log),
|
||||||
|
'msg' => 'access_denied'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
switch ($_data["tfa_method"]) {
|
switch ($_data["tfa_method"]) {
|
||||||
case "yubi_otp":
|
case "yubi_otp":
|
||||||
@@ -1220,8 +1249,7 @@ function set_tfa($_data) {
|
|||||||
$yubico_modhex_id = substr($_data["otp_token"], 0, 12);
|
$yubico_modhex_id = substr($_data["otp_token"], 0, 12);
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa`
|
$stmt = $pdo->prepare("DELETE FROM `tfa`
|
||||||
WHERE `username` = :username
|
WHERE `username` = :username
|
||||||
AND (`authmech` != 'yubi_otp')
|
AND (`authmech` = 'yubi_otp' AND `secret` LIKE :modhex)");
|
||||||
OR (`authmech` = 'yubi_otp' AND `secret` LIKE :modhex)");
|
|
||||||
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
||||||
$stmt = $pdo->prepare("INSERT INTO `tfa` (`key_id`, `username`, `authmech`, `active`, `secret`) VALUES
|
$stmt = $pdo->prepare("INSERT INTO `tfa` (`key_id`, `username`, `authmech`, `active`, `secret`) VALUES
|
||||||
(:key_id, :username, 'yubi_otp', '1', :secret)");
|
(:key_id, :username, 'yubi_otp', '1', :secret)");
|
||||||
@@ -1265,9 +1293,6 @@ function set_tfa($_data) {
|
|||||||
case "webauthn":
|
case "webauthn":
|
||||||
$key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
|
$key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
|
||||||
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `authmech` != 'webauthn'");
|
|
||||||
$stmt->execute(array(':username' => $username));
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`)
|
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`)
|
||||||
VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')");
|
VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
@@ -1439,25 +1464,27 @@ function unset_tfa_key($_data) {
|
|||||||
global $pdo;
|
global $pdo;
|
||||||
global $lang;
|
global $lang;
|
||||||
$_data_log = $_data;
|
$_data_log = $_data;
|
||||||
|
$access_denied = null;
|
||||||
$id = intval($_data['unset_tfa_key']);
|
$id = intval($_data['unset_tfa_key']);
|
||||||
$username = $_SESSION['mailcow_cc_username'];
|
$username = $_SESSION['mailcow_cc_username'];
|
||||||
if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
|
|
||||||
$_SESSION['return'][] = array(
|
// check for empty user and role
|
||||||
'type' => 'danger',
|
if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) $access_denied = true;
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
|
||||||
'msg' => 'access_denied'
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
if (!is_numeric($id)) {
|
if (!is_numeric($id)) $access_denied = true;
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
|
// set access_denied error
|
||||||
|
if ($access_denied){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_data_log),
|
'log' => array(__FUNCTION__, $_data_log),
|
||||||
'msg' => 'access_denied'
|
'msg' => 'access_denied'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if it's last key
|
||||||
$stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
|
$stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
|
||||||
WHERE `username` = :username AND `active` = '1'");
|
WHERE `username` = :username AND `active` = '1'");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
@@ -1470,6 +1497,8 @@ function unset_tfa_key($_data) {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete key
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id");
|
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id");
|
||||||
$stmt->execute(array(':username' => $username, ':id' => $id));
|
$stmt->execute(array(':username' => $username, ':id' => $id));
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
@@ -1487,7 +1516,7 @@ function unset_tfa_key($_data) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function get_tfa($username = null) {
|
function get_tfa($username = null, $id = null) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
if (isset($_SESSION['mailcow_cc_username'])) {
|
if (isset($_SESSION['mailcow_cc_username'])) {
|
||||||
$username = $_SESSION['mailcow_cc_username'];
|
$username = $_SESSION['mailcow_cc_username'];
|
||||||
@@ -1495,95 +1524,119 @@ function get_tfa($username = null) {
|
|||||||
elseif (empty($username)) {
|
elseif (empty($username)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("SELECT * FROM `tfa`
|
|
||||||
WHERE `username` = :username AND `active` = '1'");
|
|
||||||
$stmt->execute(array(':username' => $username));
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (isset($row["authmech"])) {
|
if (!isset($id)){
|
||||||
switch ($row["authmech"]) {
|
// fetch all tfa methods - just get information about possible authenticators
|
||||||
case "yubi_otp":
|
$stmt = $pdo->prepare("SELECT `id`, `key_id`, `authmech` FROM `tfa`
|
||||||
$data['name'] = "yubi_otp";
|
WHERE `username` = :username AND `active` = '1'");
|
||||||
$data['pretty'] = "Yubico OTP";
|
$stmt->execute(array(':username' => $username));
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `key_id`, RIGHT(`secret`, 12) AS 'modhex' FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username");
|
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
$stmt->execute(array(
|
|
||||||
':username' => $username,
|
// no tfa methods found
|
||||||
));
|
if (count($results) == 0) {
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$data['name'] = 'none';
|
||||||
while($row = array_shift($rows)) {
|
$data['pretty'] = "-";
|
||||||
$data['additional'][] = $row;
|
$data['additional'] = array();
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['additional'] = $results;
|
||||||
|
return $data;
|
||||||
|
} else {
|
||||||
|
// fetch specific authenticator details by id
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM `tfa`
|
||||||
|
WHERE `username` = :username AND `id` = :id AND `active` = '1'");
|
||||||
|
$stmt->execute(array(':username' => $username, ':id' => $id));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (isset($row["authmech"])) {
|
||||||
|
switch ($row["authmech"]) {
|
||||||
|
case "yubi_otp":
|
||||||
|
$data['name'] = "yubi_otp";
|
||||||
|
$data['pretty'] = "Yubico OTP";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id`, RIGHT(`secret`, 12) AS 'modhex' FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username AND `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
// u2f - deprecated, should be removed
|
||||||
|
case "u2f":
|
||||||
|
$data['name'] = "u2f";
|
||||||
|
$data['pretty'] = "Fido U2F";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username AND `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
case "hotp":
|
||||||
|
$data['name'] = "hotp";
|
||||||
|
$data['pretty'] = "HMAC-based OTP";
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
case "totp":
|
||||||
|
$data['name'] = "totp";
|
||||||
|
$data['pretty'] = "Time-based OTP";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id`, `secret` FROM `tfa` WHERE `authmech` = 'totp' AND `username` = :username AND `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
case "webauthn":
|
||||||
|
$data['name'] = "webauthn";
|
||||||
|
$data['pretty'] = "WebAuthn";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'webauthn' AND `username` = :username AND `id` = :id");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':id' => $id
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$data['name'] = 'none';
|
||||||
|
$data['pretty'] = "-";
|
||||||
|
return $data;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return $data;
|
}
|
||||||
break;
|
else {
|
||||||
// u2f - deprecated, should be removed
|
|
||||||
case "u2f":
|
|
||||||
$data['name'] = "u2f";
|
|
||||||
$data['pretty'] = "Fido U2F";
|
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
|
|
||||||
$stmt->execute(array(
|
|
||||||
':username' => $username,
|
|
||||||
));
|
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
while($row = array_shift($rows)) {
|
|
||||||
$data['additional'][] = $row;
|
|
||||||
}
|
|
||||||
return $data;
|
|
||||||
break;
|
|
||||||
case "hotp":
|
|
||||||
$data['name'] = "hotp";
|
|
||||||
$data['pretty'] = "HMAC-based OTP";
|
|
||||||
return $data;
|
|
||||||
break;
|
|
||||||
case "totp":
|
|
||||||
$data['name'] = "totp";
|
|
||||||
$data['pretty'] = "Time-based OTP";
|
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `key_id`, `secret` FROM `tfa` WHERE `authmech` = 'totp' AND `username` = :username");
|
|
||||||
$stmt->execute(array(
|
|
||||||
':username' => $username,
|
|
||||||
));
|
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
while($row = array_shift($rows)) {
|
|
||||||
$data['additional'][] = $row;
|
|
||||||
}
|
|
||||||
return $data;
|
|
||||||
break;
|
|
||||||
case "webauthn":
|
|
||||||
$data['name'] = "webauthn";
|
|
||||||
$data['pretty'] = "WebAuthn";
|
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'webauthn' AND `username` = :username");
|
|
||||||
$stmt->execute(array(
|
|
||||||
':username' => $username,
|
|
||||||
));
|
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
while($row = array_shift($rows)) {
|
|
||||||
$data['additional'][] = $row;
|
|
||||||
}
|
|
||||||
return $data;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$data['name'] = 'none';
|
$data['name'] = 'none';
|
||||||
$data['pretty'] = "-";
|
$data['pretty'] = "-";
|
||||||
return $data;
|
return $data;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
$data['name'] = 'none';
|
|
||||||
$data['pretty'] = "-";
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
function verify_tfa_login($username, $_data, $WebAuthn) {
|
function verify_tfa_login($username, $_data) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
global $yubi;
|
global $yubi;
|
||||||
global $u2f;
|
global $u2f;
|
||||||
global $tfa;
|
global $tfa;
|
||||||
$stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
|
global $WebAuthn;
|
||||||
WHERE `username` = :username AND `active` = '1'");
|
|
||||||
$stmt->execute(array(':username' => $username));
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
switch ($row["authmech"]) {
|
if ($_data['tfa_method'] != 'u2f'){
|
||||||
|
|
||||||
|
switch ($_data["tfa_method"]) {
|
||||||
case "yubi_otp":
|
case "yubi_otp":
|
||||||
if (!ctype_alnum($_data['token']) || strlen($_data['token']) != 44) {
|
if (!ctype_alnum($_data['token']) || strlen($_data['token']) != 44) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
@@ -1597,7 +1650,7 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
||||||
WHERE `username` = :username
|
WHERE `username` = :username
|
||||||
AND `authmech` = 'yubi_otp'
|
AND `authmech` = 'yubi_otp'
|
||||||
AND `active`='1'
|
AND `active` = '1'
|
||||||
AND `secret` LIKE :modhex");
|
AND `secret` LIKE :modhex");
|
||||||
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
@@ -1632,15 +1685,16 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case "totp":
|
case "totp":
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
||||||
WHERE `username` = :username
|
WHERE `username` = :username
|
||||||
AND `authmech` = 'totp'
|
AND `authmech` = 'totp'
|
||||||
|
AND `id` = :id
|
||||||
AND `active`='1'");
|
AND `active`='1'");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username, ':id' => $_data['id']));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
if ($tfa->verifyCode($row['secret'], $_data['token']) === true) {
|
if ($tfa->verifyCode($row['secret'], $_data['token']) === true) {
|
||||||
$_SESSION['tfa_id'] = $row['id'];
|
$_SESSION['tfa_id'] = $row['id'];
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
@@ -1648,7 +1702,7 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
'msg' => 'verified_totp_login'
|
'msg' => 'verified_totp_login'
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
@@ -1656,23 +1710,16 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
'msg' => 'totp_verification_failed'
|
'msg' => 'totp_verification_failed'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
'msg' => array('mysql_error', $e)
|
'msg' => array('mysql_error', $e)
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// u2f - deprecated, should be removed
|
|
||||||
case "u2f":
|
|
||||||
// delete old keys that used u2f
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `authmech` = :authmech AND `username` = :username");
|
|
||||||
$stmt->execute(array(':authmech' => 'u2f', ':username' => $username));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
case "webauthn":
|
case "webauthn":
|
||||||
$tokenData = json_decode($_data['token']);
|
$tokenData = json_decode($_data['token']);
|
||||||
$clientDataJSON = base64_decode($tokenData->clientDataJSON);
|
$clientDataJSON = base64_decode($tokenData->clientDataJSON);
|
||||||
@@ -1681,13 +1728,20 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
$id = base64_decode($tokenData->id);
|
$id = base64_decode($tokenData->id);
|
||||||
$challenge = $_SESSION['challenge'];
|
$challenge = $_SESSION['challenge'];
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `keyHandle` = :tokenId");
|
$stmt = $pdo->prepare("SELECT `id`, `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `id` = :id AND `active`='1'");
|
||||||
$stmt->execute(array(':tokenId' => $tokenData->id));
|
$stmt->execute(array(':id' => $_data['id']));
|
||||||
$process_webauthn = $stmt->fetch(PDO::FETCH_ASSOC);
|
$process_webauthn = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if (empty($process_webauthn) || empty($process_webauthn['publicKey']) || empty($process_webauthn['username'])) return false;
|
if (empty($process_webauthn)){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
|
'msg' => array('webauthn_verification_failed', 'authenticator not found')
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ($process_webauthn['publicKey'] === false) {
|
if (empty($process_webauthn['publicKey']) || $process_webauthn['publicKey'] === false) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
@@ -1695,6 +1749,7 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
|
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
|
||||||
}
|
}
|
||||||
@@ -1707,26 +1762,31 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
|
$stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $process_webauthn['username']));
|
$stmt->execute(array(':username' => $process_webauthn['username']));
|
||||||
$obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
|
$obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if ($obj_props['superadmin'] === 1) {
|
if ($obj_props['superadmin'] === 1) {
|
||||||
$_SESSION["mailcow_cc_role"] = "admin";
|
$_SESSION["mailcow_cc_role"] = "admin";
|
||||||
}
|
}
|
||||||
elseif ($obj_props['superadmin'] === 0) {
|
elseif ($obj_props['superadmin'] === 0) {
|
||||||
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
|
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $process_webauthn['username']));
|
$stmt->execute(array(':username' => $process_webauthn['username']));
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if ($row['username'] == $process_webauthn['username']) {
|
if (!empty($row['username'])) {
|
||||||
$_SESSION["mailcow_cc_role"] = "user";
|
$_SESSION["mailcow_cc_role"] = "user";
|
||||||
}
|
} else {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
|
'msg' => array('webauthn_verification_failed', 'could not determine user role')
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
@@ -1736,9 +1796,8 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
||||||
$_SESSION['tfa_id'] = $process_webauthn['key_id'];
|
$_SESSION['tfa_id'] = $process_webauthn['id'];
|
||||||
$_SESSION['authReq'] = null;
|
$_SESSION['authReq'] = null;
|
||||||
unset($_SESSION["challenge"]);
|
unset($_SESSION["challenge"]);
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
@@ -1759,6 +1818,17 @@ function verify_tfa_login($username, $_data, $WebAuthn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
// delete old keys that used u2f
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
if (count($rows) == 0) return false;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function admin_api($access, $action, $data = null) {
|
function admin_api($access, $action, $data = null) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|||||||
@@ -338,9 +338,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
$custom_params = (empty(trim($_data['custom_params']))) ? '' : trim($_data['custom_params']);
|
$custom_params = (empty(trim($_data['custom_params']))) ? '' : trim($_data['custom_params']);
|
||||||
|
|
||||||
// validate custom params
|
// validate custom params
|
||||||
foreach (explode(' -', $custom_params) as $param){
|
foreach (explode('-', $custom_params) as $param){
|
||||||
if(empty($param)) continue;
|
if(empty($param)) continue;
|
||||||
|
|
||||||
|
// extract option
|
||||||
|
if (str_contains($param, '=')) $param = explode('=', $param)[0];
|
||||||
|
else $param = rtrim($param, ' ');
|
||||||
|
// remove first char if first char is -
|
||||||
|
if ($param[0] == '-') $param = ltrim($param, $param[0]);
|
||||||
|
|
||||||
if (str_contains($param, ' ')) {
|
if (str_contains($param, ' ')) {
|
||||||
// bad char
|
// bad char
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
@@ -351,11 +357,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract option
|
|
||||||
if (str_contains($param, '=')) $param = explode('=', $param)[0];
|
|
||||||
// remove first char if first char is -
|
|
||||||
if ($param[0] == '-') $param = ltrim($param, $param[0]);
|
|
||||||
|
|
||||||
// check if param is whitelisted
|
// check if param is whitelisted
|
||||||
if (!in_array(strtolower($param), $GLOBALS["IMAPSYNC_OPTIONS"]["whitelist"])){
|
if (!in_array(strtolower($param), $GLOBALS["IMAPSYNC_OPTIONS"]["whitelist"])){
|
||||||
// bad option
|
// bad option
|
||||||
@@ -1793,9 +1794,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validate custom params
|
// validate custom params
|
||||||
foreach (explode(' -', $custom_params) as $param){
|
foreach (explode('-', $custom_params) as $param){
|
||||||
if(empty($param)) continue;
|
if(empty($param)) continue;
|
||||||
|
|
||||||
|
// extract option
|
||||||
|
if (str_contains($param, '=')) $param = explode('=', $param)[0];
|
||||||
|
else $param = rtrim($param, ' ');
|
||||||
|
// remove first char if first char is -
|
||||||
|
if ($param[0] == '-') $param = ltrim($param, $param[0]);
|
||||||
|
|
||||||
if (str_contains($param, ' ')) {
|
if (str_contains($param, ' ')) {
|
||||||
// bad char
|
// bad char
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
@@ -1806,11 +1813,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract option
|
|
||||||
if (str_contains($param, '=')) $param = explode('=', $param)[0];
|
|
||||||
// remove first char if first char is -
|
|
||||||
if ($param[0] == '-') $param = ltrim($param, $param[0]);
|
|
||||||
|
|
||||||
// check if param is whitelisted
|
// check if param is whitelisted
|
||||||
if (!in_array(strtolower($param), $GLOBALS["IMAPSYNC_OPTIONS"]["whitelist"])){
|
if (!in_array(strtolower($param), $GLOBALS["IMAPSYNC_OPTIONS"]["whitelist"])){
|
||||||
// bad option
|
// bad option
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ function pushover($_action, $_data = null) {
|
|||||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
|
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
|
||||||
$evaluate_x_prio = (isset($_data['evaluate_x_prio'])) ? intval($_data['evaluate_x_prio']) : $is_now['evaluate_x_prio'];
|
$evaluate_x_prio = (isset($_data['evaluate_x_prio'])) ? intval($_data['evaluate_x_prio']) : $is_now['evaluate_x_prio'];
|
||||||
$only_x_prio = (isset($_data['only_x_prio'])) ? intval($_data['only_x_prio']) : $is_now['only_x_prio'];
|
$only_x_prio = (isset($_data['only_x_prio'])) ? intval($_data['only_x_prio']) : $is_now['only_x_prio'];
|
||||||
|
$sound = (isset($_data['sound'])) ? $_data['sound'] : $is_now['sound'];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
@@ -101,7 +102,8 @@ function pushover($_action, $_data = null) {
|
|||||||
$po_attributes = json_encode(
|
$po_attributes = json_encode(
|
||||||
array(
|
array(
|
||||||
'evaluate_x_prio' => strval(intval($evaluate_x_prio)),
|
'evaluate_x_prio' => strval(intval($evaluate_x_prio)),
|
||||||
'only_x_prio' => strval(intval($only_x_prio))
|
'only_x_prio' => strval(intval($only_x_prio)),
|
||||||
|
'sound' => strval($sound)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$stmt = $pdo->prepare("REPLACE INTO `pushover` (`username`, `key`, `attributes`, `senders_regex`, `senders`, `token`, `title`, `text`, `active`)
|
$stmt = $pdo->prepare("REPLACE INTO `pushover` (`username`, `key`, `attributes`, `senders_regex`, `senders`, `token`, `title`, `text`, `active`)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ function init_db_schema() {
|
|||||||
try {
|
try {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
|
||||||
$db_version = "18062022_1153";
|
$db_version = "17112022_2115";
|
||||||
|
|
||||||
$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));
|
||||||
@@ -738,8 +738,8 @@ function init_db_schema() {
|
|||||||
"username" => "VARCHAR(255) NOT NULL",
|
"username" => "VARCHAR(255) NOT NULL",
|
||||||
"authmech" => "ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')",
|
"authmech" => "ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')",
|
||||||
"secret" => "VARCHAR(255) DEFAULT NULL",
|
"secret" => "VARCHAR(255) DEFAULT NULL",
|
||||||
"keyHandle" => "VARCHAR(255) DEFAULT NULL",
|
"keyHandle" => "VARCHAR(1023) DEFAULT NULL",
|
||||||
"publicKey" => "VARCHAR(255) DEFAULT NULL",
|
"publicKey" => "VARCHAR(4096) DEFAULT NULL",
|
||||||
"counter" => "INT NOT NULL DEFAULT '0'",
|
"counter" => "INT NOT NULL DEFAULT '0'",
|
||||||
"certificate" => "TEXT",
|
"certificate" => "TEXT",
|
||||||
"active" => "TINYINT(1) NOT NULL DEFAULT '0'"
|
"active" => "TINYINT(1) NOT NULL DEFAULT '0'"
|
||||||
@@ -1227,7 +1227,7 @@ function init_db_schema() {
|
|||||||
$pdo->query($create);
|
$pdo->query($create);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mitigate imapsync pipemess 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%'
|
||||||
OR custom_params LIKE '%skipmess%'
|
OR custom_params LIKE '%skipmess%'
|
||||||
@@ -1237,8 +1237,7 @@ function init_db_schema() {
|
|||||||
OR custom_params LIKE '%pipemess%'
|
OR custom_params LIKE '%pipemess%'
|
||||||
OR custom_params LIKE '%regextrans2%'
|
OR custom_params LIKE '%regextrans2%'
|
||||||
OR custom_params LIKE '%maxlinelengthcmd%';");
|
OR custom_params LIKE '%maxlinelengthcmd%';");
|
||||||
|
|
||||||
|
|
||||||
// Migrate webauthn tfa
|
// Migrate webauthn tfa
|
||||||
$stmt = $pdo->query("ALTER TABLE `tfa` MODIFY COLUMN `authmech` ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')");
|
$stmt = $pdo->query("ALTER TABLE `tfa` MODIFY COLUMN `authmech` ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')");
|
||||||
|
|
||||||
@@ -1265,6 +1264,7 @@ function init_db_schema() {
|
|||||||
$pdo->query("UPDATE `pushover` SET `attributes` = '{}' WHERE `attributes` = '' OR `attributes` IS NULL;");
|
$pdo->query("UPDATE `pushover` SET `attributes` = '{}' WHERE `attributes` = '' OR `attributes` IS NULL;");
|
||||||
$pdo->query("UPDATE `pushover` SET `attributes` = JSON_SET(`attributes`, '$.evaluate_x_prio', \"0\") WHERE JSON_VALUE(`attributes`, '$.evaluate_x_prio') IS NULL;");
|
$pdo->query("UPDATE `pushover` SET `attributes` = JSON_SET(`attributes`, '$.evaluate_x_prio', \"0\") WHERE JSON_VALUE(`attributes`, '$.evaluate_x_prio') IS NULL;");
|
||||||
$pdo->query("UPDATE `pushover` SET `attributes` = JSON_SET(`attributes`, '$.only_x_prio', \"0\") WHERE JSON_VALUE(`attributes`, '$.only_x_prio') IS NULL;");
|
$pdo->query("UPDATE `pushover` SET `attributes` = JSON_SET(`attributes`, '$.only_x_prio', \"0\") WHERE JSON_VALUE(`attributes`, '$.only_x_prio') IS NULL;");
|
||||||
|
$pdo->query("UPDATE `pushover` SET `attributes` = JSON_SET(`attributes`, '$.sound', \"pushover\") WHERE JSON_VALUE(`attributes`, '$.sound') IS NULL;");
|
||||||
// mailbox
|
// mailbox
|
||||||
$pdo->query("UPDATE `mailbox` SET `attributes` = '{}' WHERE `attributes` = '' OR `attributes` IS NULL;");
|
$pdo->query("UPDATE `mailbox` SET `attributes` = '{}' WHERE `attributes` = '' OR `attributes` IS NULL;");
|
||||||
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.passwd_update', \"0\") WHERE JSON_VALUE(`attributes`, '$.passwd_update') IS NULL;");
|
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.passwd_update', \"0\") WHERE JSON_VALUE(`attributes`, '$.passwd_update') IS NULL;");
|
||||||
|
|||||||
Generated
+8
-8
@@ -1604,16 +1604,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/twig",
|
"name": "twig/twig",
|
||||||
"version": "v3.3.8",
|
"version": "v3.4.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/twigphp/Twig.git",
|
"url": "https://github.com/twigphp/Twig.git",
|
||||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
|
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
|
"url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
|
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1628,7 +1628,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.3-dev"
|
"dev-master": "3.4-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -1664,7 +1664,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/twigphp/Twig/issues",
|
"issues": "https://github.com/twigphp/Twig/issues",
|
||||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
|
"source": "https://github.com/twigphp/Twig/tree/v3.4.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1676,7 +1676,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-02-04T06:59:48+00:00"
|
"time": "2022-09-28T08:42:51+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "yubico/u2flib-server",
|
"name": "yubico/u2flib-server",
|
||||||
@@ -1728,5 +1728,5 @@
|
|||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": [],
|
"platform": [],
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "2.2.0"
|
"plugin-api-version": "2.3.0"
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+5
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
// autoload.php @generated by Composer
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
require_once __DIR__ . '/composer/autoload_real.php';
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
return ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b::getLoader();
|
return ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b::getLoader();
|
||||||
|
|||||||
+9
-7
@@ -21,12 +21,14 @@ use Composer\Semver\VersionParser;
|
|||||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
*
|
*
|
||||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*
|
||||||
|
* @final
|
||||||
*/
|
*/
|
||||||
class InstalledVersions
|
class InstalledVersions
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var mixed[]|null
|
* @var mixed[]|null
|
||||||
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
*/
|
*/
|
||||||
private static $installed;
|
private static $installed;
|
||||||
|
|
||||||
@@ -37,7 +39,7 @@ class InstalledVersions
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array[]
|
* @var array[]
|
||||||
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
*/
|
*/
|
||||||
private static $installedByVendor = array();
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
@@ -241,7 +243,7 @@ class InstalledVersions
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
|
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||||
*/
|
*/
|
||||||
public static function getRootPackage()
|
public static function getRootPackage()
|
||||||
{
|
{
|
||||||
@@ -255,7 +257,7 @@ class InstalledVersions
|
|||||||
*
|
*
|
||||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
* @return array[]
|
* @return array[]
|
||||||
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
|
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||||
*/
|
*/
|
||||||
public static function getRawData()
|
public static function getRawData()
|
||||||
{
|
{
|
||||||
@@ -278,7 +280,7 @@ class InstalledVersions
|
|||||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
*
|
*
|
||||||
* @return array[]
|
* @return array[]
|
||||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
*/
|
*/
|
||||||
public static function getAllRawData()
|
public static function getAllRawData()
|
||||||
{
|
{
|
||||||
@@ -301,7 +303,7 @@ class InstalledVersions
|
|||||||
* @param array[] $data A vendor/composer/installed.php data set
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
* @return void
|
* @return void
|
||||||
*
|
*
|
||||||
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
|
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||||
*/
|
*/
|
||||||
public static function reload($data)
|
public static function reload($data)
|
||||||
{
|
{
|
||||||
@@ -311,7 +313,7 @@ class InstalledVersions
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array[]
|
||||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
*/
|
*/
|
||||||
private static function getInstalled()
|
private static function getInstalled()
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_classmap.php @generated by Composer
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
$vendorDir = dirname(__DIR__);
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
+2
-2
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
// autoload_files.php @generated by Composer
|
// autoload_files.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
$vendorDir = dirname(__DIR__);
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
|
||||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||||
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
||||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||||
|
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||||
'fe62ba7e10580d903cc46d808b5961a4' => $vendorDir . '/tightenco/collect/src/Collect/Support/helpers.php',
|
'fe62ba7e10580d903cc46d808b5961a4' => $vendorDir . '/tightenco/collect/src/Collect/Support/helpers.php',
|
||||||
'caf31cc6ec7cf2241cb6f12c226c3846' => $vendorDir . '/tightenco/collect/src/Collect/Support/alias.php',
|
'caf31cc6ec7cf2241cb6f12c226c3846' => $vendorDir . '/tightenco/collect/src/Collect/Support/alias.php',
|
||||||
'04c6c5c2f7095ccf6c481d3e53e1776f' => $vendorDir . '/mustangostang/spyc/Spyc.php',
|
'04c6c5c2f7095ccf6c481d3e53e1776f' => $vendorDir . '/mustangostang/spyc/Spyc.php',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_namespaces.php @generated by Composer
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
$vendorDir = dirname(__DIR__);
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_psr4.php @generated by Composer
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
$vendorDir = dirname(__DIR__);
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
+4
-27
@@ -25,38 +25,15 @@ class ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b
|
|||||||
require __DIR__ . '/platform_check.php';
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
spl_autoload_register(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'), true, true);
|
spl_autoload_register(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'), true, true);
|
||||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
spl_autoload_unregister(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'));
|
spl_autoload_unregister(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'));
|
||||||
|
|
||||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
require __DIR__ . '/autoload_static.php';
|
||||||
if ($useStaticLoader) {
|
call_user_func(\Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::getInitializer($loader));
|
||||||
require __DIR__ . '/autoload_static.php';
|
|
||||||
|
|
||||||
call_user_func(\Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::getInitializer($loader));
|
|
||||||
} else {
|
|
||||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
|
||||||
foreach ($map as $namespace => $path) {
|
|
||||||
$loader->set($namespace, $path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$map = require __DIR__ . '/autoload_psr4.php';
|
|
||||||
foreach ($map as $namespace => $path) {
|
|
||||||
$loader->setPsr4($namespace, $path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
|
||||||
if ($classMap) {
|
|
||||||
$loader->addClassMap($classMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$loader->register(true);
|
$loader->register(true);
|
||||||
|
|
||||||
if ($useStaticLoader) {
|
$includeFiles = \Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::$files;
|
||||||
$includeFiles = Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::$files;
|
|
||||||
} else {
|
|
||||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
|
||||||
}
|
|
||||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||||
composerRequire873464e4bd965a3168f133248b1b218b($fileIdentifier, $file);
|
composerRequire873464e4bd965a3168f133248b1b218b($fileIdentifier, $file);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -8,10 +8,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
|
|||||||
{
|
{
|
||||||
public static $files = array (
|
public static $files = array (
|
||||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
|
||||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||||
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
|
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
|
||||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||||
|
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||||
'fe62ba7e10580d903cc46d808b5961a4' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/helpers.php',
|
'fe62ba7e10580d903cc46d808b5961a4' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/helpers.php',
|
||||||
'caf31cc6ec7cf2241cb6f12c226c3846' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/alias.php',
|
'caf31cc6ec7cf2241cb6f12c226c3846' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/alias.php',
|
||||||
'04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php',
|
'04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php',
|
||||||
|
|||||||
+8
-8
@@ -1654,17 +1654,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/twig",
|
"name": "twig/twig",
|
||||||
"version": "v3.3.8",
|
"version": "v3.4.3",
|
||||||
"version_normalized": "3.3.8.0",
|
"version_normalized": "3.4.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/twigphp/Twig.git",
|
"url": "https://github.com/twigphp/Twig.git",
|
||||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
|
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
|
"url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
|
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1676,11 +1676,11 @@
|
|||||||
"psr/container": "^1.0",
|
"psr/container": "^1.0",
|
||||||
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
|
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
|
||||||
},
|
},
|
||||||
"time": "2022-02-04T06:59:48+00:00",
|
"time": "2022-09-28T08:42:51+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.3-dev"
|
"dev-master": "3.4-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"installation-source": "dist",
|
"installation-source": "dist",
|
||||||
@@ -1717,7 +1717,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/twigphp/Twig/issues",
|
"issues": "https://github.com/twigphp/Twig/issues",
|
||||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
|
"source": "https://github.com/twigphp/Twig/tree/v3.4.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|||||||
+34
-34
@@ -1,49 +1,49 @@
|
|||||||
<?php return array(
|
<?php return array(
|
||||||
'root' => array(
|
'root' => array(
|
||||||
'pretty_version' => '1.0.0+no-version-set',
|
'name' => '__root__',
|
||||||
'version' => '1.0.0.0',
|
'pretty_version' => 'dev-master',
|
||||||
|
'version' => 'dev-master',
|
||||||
|
'reference' => '8e0b1d8aee4af02311692cb031695cc2ac3850fd',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => NULL,
|
|
||||||
'name' => '__root__',
|
|
||||||
'dev' => true,
|
'dev' => true,
|
||||||
),
|
),
|
||||||
'versions' => array(
|
'versions' => array(
|
||||||
'__root__' => array(
|
'__root__' => array(
|
||||||
'pretty_version' => '1.0.0+no-version-set',
|
'pretty_version' => 'dev-master',
|
||||||
'version' => '1.0.0.0',
|
'version' => 'dev-master',
|
||||||
|
'reference' => '8e0b1d8aee4af02311692cb031695cc2ac3850fd',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => NULL,
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'bshaffer/oauth2-server-php' => array(
|
'bshaffer/oauth2-server-php' => array(
|
||||||
'pretty_version' => 'v1.11.1',
|
'pretty_version' => 'v1.11.1',
|
||||||
'version' => '1.11.1.0',
|
'version' => '1.11.1.0',
|
||||||
|
'reference' => '5a0c8000d4763b276919e2106f54eddda6bc50fa',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../bshaffer/oauth2-server-php',
|
'install_path' => __DIR__ . '/../bshaffer/oauth2-server-php',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '5a0c8000d4763b276919e2106f54eddda6bc50fa',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'ddeboer/imap' => array(
|
'ddeboer/imap' => array(
|
||||||
'pretty_version' => '1.13.1',
|
'pretty_version' => '1.13.1',
|
||||||
'version' => '1.13.1.0',
|
'version' => '1.13.1.0',
|
||||||
|
'reference' => '8b772d04b1deadb5df13782fb78c4b648f77496e',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../ddeboer/imap',
|
'install_path' => __DIR__ . '/../ddeboer/imap',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '8b772d04b1deadb5df13782fb78c4b648f77496e',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'directorytree/ldaprecord' => array(
|
'directorytree/ldaprecord' => array(
|
||||||
'pretty_version' => 'v2.10.1',
|
'pretty_version' => 'v2.10.1',
|
||||||
'version' => '2.10.1.0',
|
'version' => '2.10.1.0',
|
||||||
|
'reference' => 'bf512d9af7a7b0e2ed7a666ab29cefdd027bee88',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../directorytree/ldaprecord',
|
'install_path' => __DIR__ . '/../directorytree/ldaprecord',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'bf512d9af7a7b0e2ed7a666ab29cefdd027bee88',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'exorus/php-mime-mail-parser' => array(
|
'exorus/php-mime-mail-parser' => array(
|
||||||
@@ -55,28 +55,28 @@
|
|||||||
'illuminate/contracts' => array(
|
'illuminate/contracts' => array(
|
||||||
'pretty_version' => 'v9.3.0',
|
'pretty_version' => 'v9.3.0',
|
||||||
'version' => '9.3.0.0',
|
'version' => '9.3.0.0',
|
||||||
|
'reference' => 'bf4b3c254c49d28157645d01e4883b5951b1e1d0',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../illuminate/contracts',
|
'install_path' => __DIR__ . '/../illuminate/contracts',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'bf4b3c254c49d28157645d01e4883b5951b1e1d0',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'matthiasmullie/minify' => array(
|
'matthiasmullie/minify' => array(
|
||||||
'pretty_version' => '1.3.66',
|
'pretty_version' => '1.3.66',
|
||||||
'version' => '1.3.66.0',
|
'version' => '1.3.66.0',
|
||||||
|
'reference' => '45fd3b0f1dfa2c965857c6d4a470bea52adc31a6',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../matthiasmullie/minify',
|
'install_path' => __DIR__ . '/../matthiasmullie/minify',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '45fd3b0f1dfa2c965857c6d4a470bea52adc31a6',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'matthiasmullie/path-converter' => array(
|
'matthiasmullie/path-converter' => array(
|
||||||
'pretty_version' => '1.1.3',
|
'pretty_version' => '1.1.3',
|
||||||
'version' => '1.1.3.0',
|
'version' => '1.1.3.0',
|
||||||
|
'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../matthiasmullie/path-converter',
|
'install_path' => __DIR__ . '/../matthiasmullie/path-converter',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'messaged/php-mime-mail-parser' => array(
|
'messaged/php-mime-mail-parser' => array(
|
||||||
@@ -88,136 +88,136 @@
|
|||||||
'mustangostang/spyc' => array(
|
'mustangostang/spyc' => array(
|
||||||
'pretty_version' => '0.6.3',
|
'pretty_version' => '0.6.3',
|
||||||
'version' => '0.6.3.0',
|
'version' => '0.6.3.0',
|
||||||
|
'reference' => '4627c838b16550b666d15aeae1e5289dd5b77da0',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../mustangostang/spyc',
|
'install_path' => __DIR__ . '/../mustangostang/spyc',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '4627c838b16550b666d15aeae1e5289dd5b77da0',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'nesbot/carbon' => array(
|
'nesbot/carbon' => array(
|
||||||
'pretty_version' => '2.57.0',
|
'pretty_version' => '2.57.0',
|
||||||
'version' => '2.57.0.0',
|
'version' => '2.57.0.0',
|
||||||
|
'reference' => '4a54375c21eea4811dbd1149fe6b246517554e78',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../nesbot/carbon',
|
'install_path' => __DIR__ . '/../nesbot/carbon',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '4a54375c21eea4811dbd1149fe6b246517554e78',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'paragonie/random_compat' => array(
|
'paragonie/random_compat' => array(
|
||||||
'pretty_version' => 'v9.99.100',
|
'pretty_version' => 'v9.99.100',
|
||||||
'version' => '9.99.100.0',
|
'version' => '9.99.100.0',
|
||||||
|
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../paragonie/random_compat',
|
'install_path' => __DIR__ . '/../paragonie/random_compat',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'php-mime-mail-parser/php-mime-mail-parser' => array(
|
'php-mime-mail-parser/php-mime-mail-parser' => array(
|
||||||
'pretty_version' => '7.0.0',
|
'pretty_version' => '7.0.0',
|
||||||
'version' => '7.0.0.0',
|
'version' => '7.0.0.0',
|
||||||
|
'reference' => '9d09a017f3f103fec8456211a4a538b80e0eca0d',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../php-mime-mail-parser/php-mime-mail-parser',
|
'install_path' => __DIR__ . '/../php-mime-mail-parser/php-mime-mail-parser',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '9d09a017f3f103fec8456211a4a538b80e0eca0d',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'phpmailer/phpmailer' => array(
|
'phpmailer/phpmailer' => array(
|
||||||
'pretty_version' => 'v6.6.0',
|
'pretty_version' => 'v6.6.0',
|
||||||
'version' => '6.6.0.0',
|
'version' => '6.6.0.0',
|
||||||
|
'reference' => 'e43bac82edc26ca04b36143a48bde1c051cfd5b1',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../phpmailer/phpmailer',
|
'install_path' => __DIR__ . '/../phpmailer/phpmailer',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'e43bac82edc26ca04b36143a48bde1c051cfd5b1',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'psr/container' => array(
|
'psr/container' => array(
|
||||||
'pretty_version' => '2.0.2',
|
'pretty_version' => '2.0.2',
|
||||||
'version' => '2.0.2.0',
|
'version' => '2.0.2.0',
|
||||||
|
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../psr/container',
|
'install_path' => __DIR__ . '/../psr/container',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'psr/log' => array(
|
'psr/log' => array(
|
||||||
'pretty_version' => '3.0.0',
|
'pretty_version' => '3.0.0',
|
||||||
'version' => '3.0.0.0',
|
'version' => '3.0.0.0',
|
||||||
|
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../psr/log',
|
'install_path' => __DIR__ . '/../psr/log',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'psr/simple-cache' => array(
|
'psr/simple-cache' => array(
|
||||||
'pretty_version' => '2.0.0',
|
'pretty_version' => '2.0.0',
|
||||||
'version' => '2.0.0.0',
|
'version' => '2.0.0.0',
|
||||||
|
'reference' => '8707bf3cea6f710bf6ef05491234e3ab06f6432a',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../psr/simple-cache',
|
'install_path' => __DIR__ . '/../psr/simple-cache',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '8707bf3cea6f710bf6ef05491234e3ab06f6432a',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'robthree/twofactorauth' => array(
|
'robthree/twofactorauth' => array(
|
||||||
'pretty_version' => '1.8.1',
|
'pretty_version' => '1.8.1',
|
||||||
'version' => '1.8.1.0',
|
'version' => '1.8.1.0',
|
||||||
|
'reference' => '5afcb45282f1c75562a48d479ecd1732c9bdb11b',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../robthree/twofactorauth',
|
'install_path' => __DIR__ . '/../robthree/twofactorauth',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '5afcb45282f1c75562a48d479ecd1732c9bdb11b',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'soundasleep/html2text' => array(
|
'soundasleep/html2text' => array(
|
||||||
'pretty_version' => '0.5.0',
|
'pretty_version' => '0.5.0',
|
||||||
'version' => '0.5.0.0',
|
'version' => '0.5.0.0',
|
||||||
|
'reference' => 'cdb89f6ffa2c4cc78f8ed9ea6ee0594a9133ccad',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../soundasleep/html2text',
|
'install_path' => __DIR__ . '/../soundasleep/html2text',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'cdb89f6ffa2c4cc78f8ed9ea6ee0594a9133ccad',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-ctype' => array(
|
'symfony/polyfill-ctype' => array(
|
||||||
'pretty_version' => 'v1.24.0',
|
'pretty_version' => 'v1.24.0',
|
||||||
'version' => '1.24.0.0',
|
'version' => '1.24.0.0',
|
||||||
|
'reference' => '30885182c981ab175d4d034db0f6f469898070ab',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '30885182c981ab175d4d034db0f6f469898070ab',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-mbstring' => array(
|
'symfony/polyfill-mbstring' => array(
|
||||||
'pretty_version' => 'v1.24.0',
|
'pretty_version' => 'v1.24.0',
|
||||||
'version' => '1.24.0.0',
|
'version' => '1.24.0.0',
|
||||||
|
'reference' => '0abb51d2f102e00a4eefcf46ba7fec406d245825',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '0abb51d2f102e00a4eefcf46ba7fec406d245825',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-php80' => array(
|
'symfony/polyfill-php80' => array(
|
||||||
'pretty_version' => 'v1.24.0',
|
'pretty_version' => 'v1.24.0',
|
||||||
'version' => '1.24.0.0',
|
'version' => '1.24.0.0',
|
||||||
|
'reference' => '57b712b08eddb97c762a8caa32c84e037892d2e9',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '57b712b08eddb97c762a8caa32c84e037892d2e9',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/translation' => array(
|
'symfony/translation' => array(
|
||||||
'pretty_version' => 'v6.0.5',
|
'pretty_version' => 'v6.0.5',
|
||||||
'version' => '6.0.5.0',
|
'version' => '6.0.5.0',
|
||||||
|
'reference' => 'e69501c71107cc3146b32aaa45f4edd0c3427875',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/translation',
|
'install_path' => __DIR__ . '/../symfony/translation',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'e69501c71107cc3146b32aaa45f4edd0c3427875',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/translation-contracts' => array(
|
'symfony/translation-contracts' => array(
|
||||||
'pretty_version' => 'v3.0.0',
|
'pretty_version' => 'v3.0.0',
|
||||||
'version' => '3.0.0.0',
|
'version' => '3.0.0.0',
|
||||||
|
'reference' => '1b6ea5a7442af5a12dba3dbd6d71034b5b234e77',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/translation-contracts',
|
'install_path' => __DIR__ . '/../symfony/translation-contracts',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '1b6ea5a7442af5a12dba3dbd6d71034b5b234e77',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/translation-implementation' => array(
|
'symfony/translation-implementation' => array(
|
||||||
@@ -229,37 +229,37 @@
|
|||||||
'symfony/var-dumper' => array(
|
'symfony/var-dumper' => array(
|
||||||
'pretty_version' => 'v6.0.5',
|
'pretty_version' => 'v6.0.5',
|
||||||
'version' => '6.0.5.0',
|
'version' => '6.0.5.0',
|
||||||
|
'reference' => '60d6a756d5f485df5e6e40b337334848f79f61ce',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/var-dumper',
|
'install_path' => __DIR__ . '/../symfony/var-dumper',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '60d6a756d5f485df5e6e40b337334848f79f61ce',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'tightenco/collect' => array(
|
'tightenco/collect' => array(
|
||||||
'pretty_version' => 'v8.83.2',
|
'pretty_version' => 'v8.83.2',
|
||||||
'version' => '8.83.2.0',
|
'version' => '8.83.2.0',
|
||||||
|
'reference' => 'd9c66d586ec2d216d8a31283d73f8df1400cc722',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../tightenco/collect',
|
'install_path' => __DIR__ . '/../tightenco/collect',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'd9c66d586ec2d216d8a31283d73f8df1400cc722',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'twig/twig' => array(
|
'twig/twig' => array(
|
||||||
'pretty_version' => 'v3.3.8',
|
'pretty_version' => 'v3.4.3',
|
||||||
'version' => '3.3.8.0',
|
'version' => '3.4.3.0',
|
||||||
|
'reference' => 'c38fd6b0b7f370c198db91ffd02e23b517426b58',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../twig/twig',
|
'install_path' => __DIR__ . '/../twig/twig',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '972d8604a92b7054828b539f2febb0211dd5945c',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'yubico/u2flib-server' => array(
|
'yubico/u2flib-server' => array(
|
||||||
'pretty_version' => '1.0.2',
|
'pretty_version' => '1.0.2',
|
||||||
'version' => '1.0.2.0',
|
'version' => '1.0.2.0',
|
||||||
|
'reference' => '55d813acf68212ad2cadecde07551600d6971939',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../yubico/u2flib-server',
|
'install_path' => __DIR__ . '/../yubico/u2flib-server',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '55d813acf68212ad2cadecde07551600d6971939',
|
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/doc/** export-ignore
|
/doc/ export-ignore
|
||||||
/extra/** export-ignore
|
/extra/ export-ignore
|
||||||
/tests export-ignore
|
/tests/ export-ignore
|
||||||
/phpunit.xml.dist export-ignore
|
/phpunit.xml.dist export-ignore
|
||||||
|
|||||||
+11
-37
@@ -9,6 +9,9 @@ on:
|
|||||||
env:
|
env:
|
||||||
SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE: 1
|
SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE: 1
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
name: "PHP ${{ matrix.php-version }}"
|
name: "PHP ${{ matrix.php-version }}"
|
||||||
@@ -25,36 +28,23 @@ jobs:
|
|||||||
- '7.4'
|
- '7.4'
|
||||||
- '8.0'
|
- '8.0'
|
||||||
- '8.1'
|
- '8.1'
|
||||||
composer-options: ['']
|
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Install PHP with extensions"
|
- name: "Install PHP with extensions"
|
||||||
uses: shivammathur/setup-php@2.7.0
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
coverage: "none"
|
coverage: "none"
|
||||||
php-version: ${{ matrix.php-version }}
|
php-version: ${{ matrix.php-version }}
|
||||||
ini-values: memory_limit=-1
|
ini-values: memory_limit=-1
|
||||||
tools: composer:v2
|
|
||||||
|
|
||||||
- name: "Add PHPUnit matcher"
|
- name: "Add PHPUnit matcher"
|
||||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||||
|
|
||||||
- name: "Set composer cache directory"
|
- run: composer install
|
||||||
id: composer-cache
|
|
||||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
|
||||||
|
|
||||||
- name: "Cache composer"
|
|
||||||
uses: actions/cache@v2.1.2
|
|
||||||
with:
|
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('composer.json') }}
|
|
||||||
restore-keys: ${{ runner.os }}-${{ matrix.php-version }}-composer-
|
|
||||||
|
|
||||||
- run: composer install ${{ matrix.composer-options }}
|
|
||||||
|
|
||||||
- name: "Install PHPUnit"
|
- name: "Install PHPUnit"
|
||||||
run: vendor/bin/simple-phpunit install
|
run: vendor/bin/simple-phpunit install
|
||||||
@@ -92,35 +82,22 @@ jobs:
|
|||||||
- 'extra/markdown-extra'
|
- 'extra/markdown-extra'
|
||||||
- 'extra/string-extra'
|
- 'extra/string-extra'
|
||||||
- 'extra/twig-extra-bundle'
|
- 'extra/twig-extra-bundle'
|
||||||
composer-options: ['']
|
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Install PHP with extensions"
|
- name: "Install PHP with extensions"
|
||||||
uses: shivammathur/setup-php@2.7.0
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
coverage: "none"
|
coverage: "none"
|
||||||
php-version: ${{ matrix.php-version }}
|
php-version: ${{ matrix.php-version }}
|
||||||
ini-values: memory_limit=-1
|
ini-values: memory_limit=-1
|
||||||
tools: composer:v2
|
|
||||||
|
|
||||||
- name: "Add PHPUnit matcher"
|
- name: "Add PHPUnit matcher"
|
||||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||||
|
|
||||||
- name: "Set composer cache directory"
|
|
||||||
id: composer-cache
|
|
||||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
|
||||||
|
|
||||||
- name: "Cache composer"
|
|
||||||
uses: actions/cache@v2.1.2
|
|
||||||
with:
|
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-${{ matrix.php-version }}-${{ matrix.extension }}-${{ hashFiles('composer.json') }}
|
|
||||||
restore-keys: ${{ runner.os }}-${{ matrix.php-version }}-${{ matrix.extension }}-
|
|
||||||
|
|
||||||
- run: composer install
|
- run: composer install
|
||||||
|
|
||||||
- name: "Install PHPUnit"
|
- name: "Install PHPUnit"
|
||||||
@@ -129,10 +106,6 @@ jobs:
|
|||||||
- name: "PHPUnit version"
|
- name: "PHPUnit version"
|
||||||
run: vendor/bin/simple-phpunit --version
|
run: vendor/bin/simple-phpunit --version
|
||||||
|
|
||||||
- if: matrix.extension == 'extra/markdown-extra' && matrix.php-version == '8.0'
|
|
||||||
working-directory: ${{ matrix.extension}}
|
|
||||||
run: composer config platform.php 7.4.99
|
|
||||||
|
|
||||||
- name: "Composer install"
|
- name: "Composer install"
|
||||||
working-directory: ${{ matrix.extension}}
|
working-directory: ${{ matrix.extension}}
|
||||||
run: composer install
|
run: composer install
|
||||||
@@ -140,6 +113,7 @@ jobs:
|
|||||||
- name: "Run tests"
|
- name: "Run tests"
|
||||||
working-directory: ${{ matrix.extension}}
|
working-directory: ${{ matrix.extension}}
|
||||||
run: ../../vendor/bin/simple-phpunit
|
run: ../../vendor/bin/simple-phpunit
|
||||||
|
|
||||||
#
|
#
|
||||||
# Drupal does not support Twig 3 now!
|
# Drupal does not support Twig 3 now!
|
||||||
#
|
#
|
||||||
@@ -160,10 +134,10 @@ jobs:
|
|||||||
#
|
#
|
||||||
# steps:
|
# steps:
|
||||||
# - name: "Checkout code"
|
# - name: "Checkout code"
|
||||||
# uses: actions/checkout@v2.3.3
|
# uses: actions/checkout@v2
|
||||||
#
|
#
|
||||||
# - name: "Install PHP with extensions"
|
# - name: "Install PHP with extensions"
|
||||||
# uses: shivammathur/setup-php@2.7.0
|
# uses: shivammathur/setup-php@2
|
||||||
# with:
|
# with:
|
||||||
# coverage: "none"
|
# coverage: "none"
|
||||||
# extensions: "gd, pdo_sqlite"
|
# extensions: "gd, pdo_sqlite"
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
|
- '2.x'
|
||||||
- '3.x'
|
- '3.x'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: "Build"
|
name: "Build"
|
||||||
@@ -16,32 +20,32 @@ jobs:
|
|||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Set up Python 3.7"
|
- name: "Set-up PHP"
|
||||||
uses: actions/setup-python@v1
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
python-version: '3.7' # Semantic version range syntax or exact version of a Python version
|
php-version: 8.1
|
||||||
|
coverage: none
|
||||||
|
tools: "composer:v2"
|
||||||
|
|
||||||
- name: "Display Python version"
|
- name: Get composer cache directory
|
||||||
run: python -c "import sys; print(sys.version)"
|
id: composercache
|
||||||
|
working-directory: doc/_build
|
||||||
|
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||||
|
|
||||||
- name: "Install Sphinx dependencies"
|
- name: Cache dependencies
|
||||||
run: sudo apt-get install python-dev build-essential
|
|
||||||
|
|
||||||
- name: "Cache pip"
|
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ${{ steps.composercache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('_build/.requirements.txt') }}
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||||
restore-keys: |
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
${{ runner.os }}-pip-
|
|
||||||
|
|
||||||
- name: "Install Sphinx + requirements via pip"
|
- name: "Install dependencies"
|
||||||
working-directory: "doc"
|
working-directory: doc/_build
|
||||||
run: pip install -r _build/.requirements.txt
|
run: composer install --prefer-dist --no-progress
|
||||||
|
|
||||||
- name: "Build documentation"
|
- name: "Build the docs"
|
||||||
working-directory: "doc"
|
working-directory: doc/_build
|
||||||
run: make -C _build SPHINXOPTS="-nqW -j auto" html
|
run: php build.php --disable-cache
|
||||||
|
|
||||||
doctor-rst:
|
doctor-rst:
|
||||||
name: "DOCtor-RST"
|
name: "DOCtor-RST"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/doc/_build/vendor
|
||||||
|
/doc/_build/output
|
||||||
/composer.lock
|
/composer.lock
|
||||||
/phpunit.xml
|
/phpunit.xml
|
||||||
/vendor
|
/vendor
|
||||||
|
|||||||
+30
@@ -1,6 +1,36 @@
|
|||||||
|
# 3.4.3 (2022-09-28)
|
||||||
|
|
||||||
|
* Fix a security issue on filesystem loader (possibility to load a template outside a configured directory)
|
||||||
|
|
||||||
|
# 3.4.2 (2022-08-12)
|
||||||
|
|
||||||
|
* Allow inherited magic method to still run with calling class
|
||||||
|
* Fix CallExpression::reflectCallable() throwing TypeError
|
||||||
|
* Fix typo in naming (currency_code)
|
||||||
|
|
||||||
|
# 3.4.1 (2022-05-17)
|
||||||
|
|
||||||
|
* Fix optimizing non-public named closures
|
||||||
|
|
||||||
|
# 3.4.0 (2022-05-22)
|
||||||
|
|
||||||
|
* Add support for named closures
|
||||||
|
|
||||||
|
# 3.3.10 (2022-04-06)
|
||||||
|
|
||||||
|
* Enable bytecode invalidation when auto_reload is enabled
|
||||||
|
|
||||||
|
# 3.3.9 (2022-03-25)
|
||||||
|
|
||||||
|
* Fix custom escapers when using multiple Twig environments
|
||||||
|
* Add support for "constant('class', object)"
|
||||||
|
* Do not reuse internally generated variable names during parsing
|
||||||
|
|
||||||
# 3.3.8 (2022-02-04)
|
# 3.3.8 (2022-02-04)
|
||||||
|
|
||||||
* Fix a security issue when in a sandbox: the `sort` filter must require a Closure for the `arrow` parameter
|
* Fix a security issue when in a sandbox: the `sort` filter must require a Closure for the `arrow` parameter
|
||||||
|
* Fix deprecation notice on `round`
|
||||||
|
* Fix call to deprecated `convertToHtml` method
|
||||||
|
|
||||||
# 3.3.7 (2022-01-03)
|
# 3.3.7 (2022-01-03)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@
|
|||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.3-dev"
|
"dev-master": "3.4-dev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -38,11 +38,11 @@ use Twig\TokenParser\TokenParserInterface;
|
|||||||
*/
|
*/
|
||||||
class Environment
|
class Environment
|
||||||
{
|
{
|
||||||
public const VERSION = '3.3.8';
|
public const VERSION = '3.4.3';
|
||||||
public const VERSION_ID = 30308;
|
public const VERSION_ID = 30403;
|
||||||
public const MAJOR_VERSION = 3;
|
public const MAJOR_VERSION = 3;
|
||||||
public const MINOR_VERSION = 3;
|
public const MINOR_VERSION = 4;
|
||||||
public const RELEASE_VERSION = 8;
|
public const RELEASE_VERSION = 3;
|
||||||
public const EXTRA_VERSION = '';
|
public const EXTRA_VERSION = '';
|
||||||
|
|
||||||
private $charset;
|
private $charset;
|
||||||
@@ -228,7 +228,7 @@ class Environment
|
|||||||
{
|
{
|
||||||
if (\is_string($cache)) {
|
if (\is_string($cache)) {
|
||||||
$this->originalCache = $cache;
|
$this->originalCache = $cache;
|
||||||
$this->cache = new FilesystemCache($cache);
|
$this->cache = new FilesystemCache($cache, $this->autoReload ? FilesystemCache::FORCE_BYTECODE_INVALIDATION : 0);
|
||||||
} elseif (false === $cache) {
|
} elseif (false === $cache) {
|
||||||
$this->originalCache = $cache;
|
$this->originalCache = $cache;
|
||||||
$this->cache = new NullCache();
|
$this->cache = new NullCache();
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ class ExpressionParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new SyntaxError('Expected name or number.', $lineno, $stream->getSourceContext());
|
throw new SyntaxError(sprintf('Expected name or number, got value "%s" of type %s.', $token->getValue(), Token::typeToEnglish($token->getType())), $lineno, $stream->getSourceContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
|
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
|
||||||
|
|||||||
@@ -1359,6 +1359,10 @@ function twig_source(Environment $env, $name, $ignoreMissing = false)
|
|||||||
function twig_constant($constant, $object = null)
|
function twig_constant($constant, $object = null)
|
||||||
{
|
{
|
||||||
if (null !== $object) {
|
if (null !== $object) {
|
||||||
|
if ('class' === $constant) {
|
||||||
|
return \get_class($object);
|
||||||
|
}
|
||||||
|
|
||||||
$constant = \get_class($object).'::'.$constant;
|
$constant = \get_class($object).'::'.$constant;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1376,6 +1380,10 @@ function twig_constant($constant, $object = null)
|
|||||||
function twig_constant_is_defined($constant, $object = null)
|
function twig_constant_is_defined($constant, $object = null)
|
||||||
{
|
{
|
||||||
if (null !== $object) {
|
if (null !== $object) {
|
||||||
|
if ('class' === $constant) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$constant = \get_class($object).'::'.$constant;
|
$constant = \get_class($object).'::'.$constant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -387,13 +387,8 @@ function twig_escape_filter(Environment $env, $string, $strategy = 'html', $char
|
|||||||
return rawurlencode($string);
|
return rawurlencode($string);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
static $escapers;
|
$escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
|
||||||
|
if (array_key_exists($strategy, $escapers)) {
|
||||||
if (null === $escapers) {
|
|
||||||
$escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($escapers[$strategy])) {
|
|
||||||
return $escapers[$strategy]($env, $string, $charset);
|
return $escapers[$strategy]($env, $string, $charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,11 +91,11 @@ final class SandboxExtension extends AbstractExtension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkPropertyAllowed($obj, $method, int $lineno = -1, Source $source = null): void
|
public function checkPropertyAllowed($obj, $property, int $lineno = -1, Source $source = null): void
|
||||||
{
|
{
|
||||||
if ($this->isSandboxed()) {
|
if ($this->isSandboxed()) {
|
||||||
try {
|
try {
|
||||||
$this->policy->checkPropertyAllowed($obj, $method);
|
$this->policy->checkPropertyAllowed($obj, $property);
|
||||||
} catch (SecurityNotAllowedPropertyError $e) {
|
} catch (SecurityNotAllowedPropertyError $e) {
|
||||||
$e->setSourceContext($source);
|
$e->setSourceContext($source);
|
||||||
$e->setTemplateLine($lineno);
|
$e->setTemplateLine($lineno);
|
||||||
|
|||||||
@@ -183,9 +183,9 @@ class FilesystemLoader implements LoaderInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->validateName($name);
|
|
||||||
|
|
||||||
list($namespace, $shortname) = $this->parseName($name);
|
list($namespace, $shortname) = $this->parseName($name);
|
||||||
|
|
||||||
|
$this->validateName($shortname);
|
||||||
} catch (LoaderError $e) {
|
} catch (LoaderError $e) {
|
||||||
if (!$throw) {
|
if (!$throw) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -24,19 +24,20 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
{
|
{
|
||||||
$callable = $this->getAttribute('callable');
|
$callable = $this->getAttribute('callable');
|
||||||
|
|
||||||
$closingParenthesis = false;
|
|
||||||
$isArray = false;
|
|
||||||
if (\is_string($callable) && false === strpos($callable, '::')) {
|
if (\is_string($callable) && false === strpos($callable, '::')) {
|
||||||
$compiler->raw($callable);
|
$compiler->raw($callable);
|
||||||
} else {
|
} else {
|
||||||
list($r, $callable) = $this->reflectCallable($callable);
|
[$r, $callable] = $this->reflectCallable($callable);
|
||||||
if ($r instanceof \ReflectionMethod && \is_string($callable[0])) {
|
|
||||||
if ($r->isStatic()) {
|
if (\is_string($callable)) {
|
||||||
|
$compiler->raw($callable);
|
||||||
|
} elseif (\is_array($callable) && \is_string($callable[0])) {
|
||||||
|
if (!$r instanceof \ReflectionMethod || $r->isStatic()) {
|
||||||
$compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
|
$compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
|
||||||
} else {
|
} else {
|
||||||
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
|
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
|
||||||
}
|
}
|
||||||
} elseif ($r instanceof \ReflectionMethod && $callable[0] instanceof ExtensionInterface) {
|
} elseif (\is_array($callable) && $callable[0] instanceof ExtensionInterface) {
|
||||||
$class = \get_class($callable[0]);
|
$class = \get_class($callable[0]);
|
||||||
if (!$compiler->getEnvironment()->hasExtension($class)) {
|
if (!$compiler->getEnvironment()->hasExtension($class)) {
|
||||||
// Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error
|
// Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error
|
||||||
@@ -47,17 +48,11 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
|
|
||||||
$compiler->raw(sprintf('->%s', $callable[1]));
|
$compiler->raw(sprintf('->%s', $callable[1]));
|
||||||
} else {
|
} else {
|
||||||
$closingParenthesis = true;
|
$compiler->raw(sprintf('$this->env->get%s(\'%s\')->getCallable()', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
|
||||||
$isArray = true;
|
|
||||||
$compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), ', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->compileArguments($compiler, $isArray);
|
$this->compileArguments($compiler);
|
||||||
|
|
||||||
if ($closingParenthesis) {
|
|
||||||
$compiler->raw(')');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function compileArguments(Compiler $compiler, $isArray = false): void
|
protected function compileArguments(Compiler $compiler, $isArray = false): void
|
||||||
@@ -244,10 +239,7 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
|
|
||||||
private function getCallableParameters($callable, bool $isVariadic): array
|
private function getCallableParameters($callable, bool $isVariadic): array
|
||||||
{
|
{
|
||||||
list($r) = $this->reflectCallable($callable);
|
[$r, , $callableName] = $this->reflectCallable($callable);
|
||||||
if (null === $r) {
|
|
||||||
return [[], false];
|
|
||||||
}
|
|
||||||
|
|
||||||
$parameters = $r->getParameters();
|
$parameters = $r->getParameters();
|
||||||
if ($this->hasNode('node')) {
|
if ($this->hasNode('node')) {
|
||||||
@@ -274,11 +266,6 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
array_pop($parameters);
|
array_pop($parameters);
|
||||||
$isPhpVariadic = true;
|
$isPhpVariadic = true;
|
||||||
} else {
|
} else {
|
||||||
$callableName = $r->name;
|
|
||||||
if ($r instanceof \ReflectionMethod) {
|
|
||||||
$callableName = $r->getDeclaringClass()->name.'::'.$callableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
|
throw new \LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,29 +279,41 @@ abstract class CallExpression extends AbstractExpression
|
|||||||
return $this->reflector;
|
return $this->reflector;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\is_array($callable)) {
|
if (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
|
||||||
if (!method_exists($callable[0], $callable[1])) {
|
$callable = [substr($callable, 0, $pos), substr($callable, 2 + $pos)];
|
||||||
// __call()
|
|
||||||
return [null, []];
|
|
||||||
}
|
|
||||||
$r = new \ReflectionMethod($callable[0], $callable[1]);
|
|
||||||
} elseif (\is_object($callable) && !$callable instanceof \Closure) {
|
|
||||||
$r = new \ReflectionObject($callable);
|
|
||||||
$r = $r->getMethod('__invoke');
|
|
||||||
$callable = [$callable, '__invoke'];
|
|
||||||
} elseif (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
|
|
||||||
$class = substr($callable, 0, $pos);
|
|
||||||
$method = substr($callable, $pos + 2);
|
|
||||||
if (!method_exists($class, $method)) {
|
|
||||||
// __staticCall()
|
|
||||||
return [null, []];
|
|
||||||
}
|
|
||||||
$r = new \ReflectionMethod($callable);
|
|
||||||
$callable = [$class, $method];
|
|
||||||
} else {
|
|
||||||
$r = new \ReflectionFunction($callable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->reflector = [$r, $callable];
|
if (\is_array($callable) && method_exists($callable[0], $callable[1])) {
|
||||||
|
$r = new \ReflectionMethod($callable[0], $callable[1]);
|
||||||
|
|
||||||
|
return $this->reflector = [$r, $callable, $r->class.'::'.$r->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
$checkVisibility = $callable instanceof \Closure;
|
||||||
|
try {
|
||||||
|
$closure = \Closure::fromCallable($callable);
|
||||||
|
} catch (\TypeError $e) {
|
||||||
|
throw new \LogicException(sprintf('Callback for %s "%s" is not callable in the current scope.', $this->getAttribute('type'), $this->getAttribute('name')), 0, $e);
|
||||||
|
}
|
||||||
|
$r = new \ReflectionFunction($closure);
|
||||||
|
|
||||||
|
if (false !== strpos($r->name, '{closure}')) {
|
||||||
|
return $this->reflector = [$r, $callable, 'Closure'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($object = $r->getClosureThis()) {
|
||||||
|
$callable = [$object, $r->name];
|
||||||
|
$callableName = (\function_exists('get_debug_type') ? get_debug_type($object) : \get_class($object)).'::'.$r->name;
|
||||||
|
} elseif ($class = $r->getClosureScopeClass()) {
|
||||||
|
$callableName = (\is_array($callable) ? $callable[0] : $class->name).'::'.$r->name;
|
||||||
|
} else {
|
||||||
|
$callable = $callableName = $r->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($checkVisibility && \is_array($callable) && method_exists(...$callable) && !(new \ReflectionMethod(...$callable))->isPublic()) {
|
||||||
|
$callable = $r->getClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->reflector = [$r, $callable, $callableName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -58,7 +58,7 @@ class Parser
|
|||||||
public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode
|
public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode
|
||||||
{
|
{
|
||||||
$vars = get_object_vars($this);
|
$vars = get_object_vars($this);
|
||||||
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']);
|
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['varNameSalt']);
|
||||||
$this->stack[] = $vars;
|
$this->stack[] = $vars;
|
||||||
|
|
||||||
// node visitors
|
// node visitors
|
||||||
@@ -78,7 +78,6 @@ class Parser
|
|||||||
$this->blockStack = [];
|
$this->blockStack = [];
|
||||||
$this->importedSymbols = [[]];
|
$this->importedSymbols = [[]];
|
||||||
$this->embeddedTemplates = [];
|
$this->embeddedTemplates = [];
|
||||||
$this->varNameSalt = 0;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$body = $this->subparse($test, $dropNeedle);
|
$body = $this->subparse($test, $dropNeedle);
|
||||||
|
|||||||
@@ -19,17 +19,27 @@ namespace Twig\Sandbox;
|
|||||||
interface SecurityPolicyInterface
|
interface SecurityPolicyInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
* @param string[] $tags
|
||||||
|
* @param string[] $filters
|
||||||
|
* @param string[] $functions
|
||||||
|
*
|
||||||
* @throws SecurityError
|
* @throws SecurityError
|
||||||
*/
|
*/
|
||||||
public function checkSecurity($tags, $filters, $functions): void;
|
public function checkSecurity($tags, $filters, $functions): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param object $obj
|
||||||
|
* @param string $method
|
||||||
|
*
|
||||||
* @throws SecurityNotAllowedMethodError
|
* @throws SecurityNotAllowedMethodError
|
||||||
*/
|
*/
|
||||||
public function checkMethodAllowed($obj, $method): void;
|
public function checkMethodAllowed($obj, $method): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param object $obj
|
||||||
|
* @param string $property
|
||||||
|
*
|
||||||
* @throws SecurityNotAllowedPropertyError
|
* @throws SecurityNotAllowedPropertyError
|
||||||
*/
|
*/
|
||||||
public function checkPropertyAllowed($obj, $method): void;
|
public function checkPropertyAllowed($obj, $property): void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,9 @@ $qrprovider = new RobThree\Auth\Providers\Qr\QRServerProvider();
|
|||||||
$tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL, 6, 30, 'sha1', $qrprovider);
|
$tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL, 6, 30, 'sha1', $qrprovider);
|
||||||
|
|
||||||
// FIDO2
|
// FIDO2
|
||||||
|
$server_name = parse_url('https://' . $_SERVER['HTTP_HOST'], PHP_URL_HOST);
|
||||||
$formats = $GLOBALS['FIDO2_FORMATS'];
|
$formats = $GLOBALS['FIDO2_FORMATS'];
|
||||||
$WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $_SERVER['HTTP_HOST'], $formats);
|
$WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $server_name, $formats);
|
||||||
// only include root ca's when needed
|
// only include root ca's when needed
|
||||||
if (getenv('WEBAUTHN_ONLY_TRUSTED_VENDORS') == 'y') $WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates');
|
if (getenv('WEBAUTHN_ONLY_TRUSTED_VENDORS') == 'y') $WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates');
|
||||||
|
|
||||||
@@ -194,9 +195,65 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
|
|||||||
// Set language
|
// Set language
|
||||||
if (!isset($_SESSION['mailcow_locale']) && !isset($_COOKIE['mailcow_locale'])) {
|
if (!isset($_SESSION['mailcow_locale']) && !isset($_COOKIE['mailcow_locale'])) {
|
||||||
if ($DETECT_LANGUAGE && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
if ($DETECT_LANGUAGE && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||||
$header_lang = strtolower(substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2));
|
// regex inspired from @GabrielAnderson on http://stackoverflow.com/questions/6038236/http-accept-language
|
||||||
if (array_key_exists($header_lang, $AVAILABLE_LANGUAGES)) {
|
preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})*)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
|
||||||
$_SESSION['mailcow_locale'] = $header_lang;
|
|
||||||
|
$langs = $lang_parse[1];
|
||||||
|
$ranks = $lang_parse[4];
|
||||||
|
|
||||||
|
// (create an associative array 'language' => 'preference')
|
||||||
|
$lang2pref = array();
|
||||||
|
for ($i=0; $i<count($langs); $i++) {
|
||||||
|
$lang2pref[strtolower($langs[$i])] = (float) (!empty($ranks[$i]) ? $ranks[$i] : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// (comparison function for uksort)
|
||||||
|
$cmpLangs = function ($a, $b) use ($lang2pref) {
|
||||||
|
if ($lang2pref[$a] > $lang2pref[$b])
|
||||||
|
return -1;
|
||||||
|
elseif ($lang2pref[$a] < $lang2pref[$b])
|
||||||
|
return 1;
|
||||||
|
elseif (strlen($a) > strlen($b))
|
||||||
|
return -1;
|
||||||
|
elseif (strlen($a) < strlen($b))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// sort the languages by prefered language and by the most specific region
|
||||||
|
uksort($lang2pref, $cmpLangs);
|
||||||
|
|
||||||
|
// generate language array without the region part
|
||||||
|
$AVAILABLE_BASE_LANGUAGES=array();
|
||||||
|
foreach ($AVAILABLE_LANGUAGES as $code => $lang) {
|
||||||
|
$base_code = substr($code, 0, 2);
|
||||||
|
if (!array_key_exists($base_code, $AVAILABLE_BASE_LANGUAGES)) {
|
||||||
|
$AVAILABLE_BASE_LANGUAGES[$base_code] = $code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a perfect match or partial match
|
||||||
|
// Match en-gb or en
|
||||||
|
foreach ($lang2pref as $lang => $q) {
|
||||||
|
if (array_key_exists($lang, $AVAILABLE_LANGUAGES)) {
|
||||||
|
$_SESSION['mailcow_locale'] = $lang;
|
||||||
|
break;
|
||||||
|
} elseif (array_key_exists($lang, $AVAILABLE_BASE_LANGUAGES)) {
|
||||||
|
$_SESSION['mailcow_locale'] = $AVAILABLE_BASE_LANGUAGES[$lang];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try suggest match
|
||||||
|
// e.g. suggest en-gb when only en-us is provided
|
||||||
|
if (!isset($_COOKIE['mailcow_locale'])) {
|
||||||
|
foreach ($lang2pref as $lang => $q) {
|
||||||
|
if (array_key_exists(substr($lang, 0, 2), $AVAILABLE_BASE_LANGUAGES)) {
|
||||||
|
$_SESSION['mailcow_locale'] = $AVAILABLE_BASE_LANGUAGES[substr($lang, 0, 2)];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -214,7 +271,7 @@ if (isset($_GET['lang']) && array_key_exists($_GET['lang'], $AVAILABLE_LANGUAGES
|
|||||||
/*
|
/*
|
||||||
* load language
|
* load language
|
||||||
*/
|
*/
|
||||||
$lang = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en.json'), true);
|
$lang = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en-gb.json'), true);
|
||||||
|
|
||||||
$langFile = $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.json';
|
$langFile = $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.json';
|
||||||
if(file_exists($langFile)) {
|
if(file_exists($langFile)) {
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
if (isset($_POST["verify_tfa_login"])) {
|
if (isset($_POST["verify_tfa_login"])) {
|
||||||
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST, $WebAuthn)) {
|
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) {
|
||||||
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
|
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
|
||||||
$_SESSION['mailcow_cc_role'] = $_SESSION['pending_mailcow_cc_role'];
|
$_SESSION['mailcow_cc_role'] = $_SESSION['pending_mailcow_cc_role'];
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
|
|
||||||
header("Location: /user");
|
header("Location: /user");
|
||||||
} else {
|
} else {
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_GET["cancel_tfa_login"])) {
|
if (isset($_GET["cancel_tfa_login"])) {
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
|
|
||||||
header("Location: /");
|
header("Location: /");
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,7 @@ if (isset($_POST["quick_delete"])) {
|
|||||||
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
||||||
$login_user = strtolower(trim($_POST["login_user"]));
|
$login_user = strtolower(trim($_POST["login_user"]));
|
||||||
$as = check_login($login_user, $_POST["pass_user"]);
|
$as = check_login($login_user, $_POST["pass_user"]);
|
||||||
|
|
||||||
if ($as == "admin") {
|
if ($as == "admin") {
|
||||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||||
$_SESSION['mailcow_cc_role'] = "admin";
|
$_SESSION['mailcow_cc_role'] = "admin";
|
||||||
@@ -47,22 +48,22 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
|||||||
elseif ($as == "user") {
|
elseif ($as == "user") {
|
||||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||||
$_SESSION['mailcow_cc_role'] = "user";
|
$_SESSION['mailcow_cc_role'] = "user";
|
||||||
$http_parameters = explode('&', $_SESSION['index_query_string']);
|
$http_parameters = explode('&', $_SESSION['index_query_string']);
|
||||||
unset($_SESSION['index_query_string']);
|
unset($_SESSION['index_query_string']);
|
||||||
if (in_array('mobileconfig', $http_parameters)) {
|
if (in_array('mobileconfig', $http_parameters)) {
|
||||||
if (in_array('only_email', $http_parameters)) {
|
if (in_array('only_email', $http_parameters)) {
|
||||||
header("Location: /mobileconfig.php?email_only");
|
header("Location: /mobileconfig.php?email_only");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
header("Location: /mobileconfig.php");
|
header("Location: /mobileconfig.php");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
header("Location: /user");
|
header("Location: /user");
|
||||||
}
|
}
|
||||||
elseif ($as != "pending") {
|
elseif ($as != "pending") {
|
||||||
unset($_SESSION['pending_mailcow_cc_username']);
|
unset($_SESSION['pending_mailcow_cc_username']);
|
||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_method']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
unset($_SESSION['mailcow_cc_username']);
|
unset($_SESSION['mailcow_cc_username']);
|
||||||
unset($_SESSION['mailcow_cc_role']);
|
unset($_SESSION['mailcow_cc_role']);
|
||||||
}
|
}
|
||||||
|
|||||||
+145
-146
@@ -76,32 +76,35 @@ $autodiscover_config = array(
|
|||||||
$DETECT_LANGUAGE = true;
|
$DETECT_LANGUAGE = true;
|
||||||
|
|
||||||
// Change default language
|
// Change default language
|
||||||
$DEFAULT_LANG = 'en';
|
$DEFAULT_LANG = 'en-gb';
|
||||||
|
|
||||||
// Available languages
|
// Available languages
|
||||||
// https://www.iso.org/obp/ui/#search
|
// https://www.iso.org/obp/ui/#search
|
||||||
// https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
|
// https://en.wikipedia.org/wiki/IETF_language_tag
|
||||||
$AVAILABLE_LANGUAGES = array(
|
$AVAILABLE_LANGUAGES = array(
|
||||||
'cs' => 'Čeština (Czech)',
|
// 'ca-es' => 'Català (Catalan)',
|
||||||
'da' => 'Danish (Dansk)',
|
'cs-cz' => 'Čeština (Czech)',
|
||||||
'de' => 'Deutsch (German)',
|
'da-dk' => 'Danish (Dansk)',
|
||||||
'en' => 'English',
|
'de-de' => 'Deutsch (German)',
|
||||||
'es' => 'Español (Spanish)',
|
'en-gb' => 'English',
|
||||||
'fi' => 'Suomi (Finish)',
|
'es-es' => 'Español (Spanish)',
|
||||||
'fr' => 'Français (French)',
|
'fi-fi' => 'Suomi (Finish)',
|
||||||
'hu' => 'Magyar (Hungarian)',
|
'fr-fr' => 'Français (French)',
|
||||||
'it' => 'Italiano (Italian)',
|
'hu-hu' => 'Magyar (Hungarian)',
|
||||||
'ko' => '한국어 (Korean)',
|
'it-it' => 'Italiano (Italian)',
|
||||||
'lv' => 'latviešu (Latvian)',
|
'ko-kr' => '한국어 (Korean)',
|
||||||
'nl' => 'Nederlands (Dutch)',
|
'lv-lv' => 'latviešu (Latvian)',
|
||||||
'pl' => 'Język Polski (Polish)',
|
'nl-nl' => 'Nederlands (Dutch)',
|
||||||
'pt' => 'Português (Portuguese)',
|
'pl-pl' => 'Język Polski (Polish)',
|
||||||
'ro' => 'Română (Romanian)',
|
'pt-pt' => 'Português (Portuguese)',
|
||||||
'ru' => 'Pусский (Russian)',
|
'ro-ro' => 'Română (Romanian)',
|
||||||
'sk' => 'Slovenčina (Slovak)',
|
'ru-ru' => 'Pусский (Russian)',
|
||||||
'sv' => 'Svenska (Swedish)',
|
'sk-sk' => 'Slovenčina (Slovak)',
|
||||||
'uk' => 'Українська (Ukrainian)',
|
'sv-se' => 'Svenska (Swedish)',
|
||||||
'zh' => '中文 (Chinese)'
|
'tr-tr' => 'Türkçe (Turkish)',
|
||||||
|
'uk-ua' => 'Українська (Ukrainian)',
|
||||||
|
'zh-cn' => '简体中文 (Simplified Chinese)',
|
||||||
|
'zh-tw' => '繁體中文 (Traditional Chinese)',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Change theme (default: lumen)
|
// Change theme (default: lumen)
|
||||||
@@ -232,131 +235,127 @@ $RSPAMD_MAPS = array(
|
|||||||
|
|
||||||
$IMAPSYNC_OPTIONS = array(
|
$IMAPSYNC_OPTIONS = array(
|
||||||
'whitelist' => array(
|
'whitelist' => array(
|
||||||
'log',
|
'authmech1',
|
||||||
'showpasswords',
|
'authmech2',
|
||||||
'nossl1',
|
'authuser1',
|
||||||
'nossl2',
|
'authuser2',
|
||||||
'ssl2',
|
'debugcontent',
|
||||||
'notls1',
|
'disarmreadreceipts',
|
||||||
'notls2',
|
'logdir',
|
||||||
'tls2',
|
'debugcrossduplicates',
|
||||||
'debugssl',
|
'maxsize',
|
||||||
'sslargs1',
|
'minsize',
|
||||||
'sslargs2',
|
'minage',
|
||||||
'authmech1',
|
'search',
|
||||||
'authmech2',
|
'noabletosearch',
|
||||||
'authuser1',
|
'pidfile',
|
||||||
'authuser2',
|
'pidfilelocking',
|
||||||
'proxyauth1',
|
'search1',
|
||||||
'proxyauth2',
|
'search2',
|
||||||
'authmd51',
|
'sslargs1',
|
||||||
'authmd52',
|
'sslargs2',
|
||||||
'domain1',
|
'syncduplicates',
|
||||||
'domain2',
|
'usecache',
|
||||||
'oauthaccesstoken1',
|
'synclabels',
|
||||||
'oauthaccesstoken2',
|
'truncmess',
|
||||||
'oauthdirect1',
|
'domino2',
|
||||||
'oauthdirect2',
|
'expunge1',
|
||||||
'folder',
|
'filterbuggyflags',
|
||||||
'folder',
|
'justconnect',
|
||||||
'folderrec',
|
'justfolders',
|
||||||
'folderrec',
|
'maxlinelength',
|
||||||
'folderfirst',
|
'useheader',
|
||||||
'folderfirst',
|
'noabletosearch1',
|
||||||
'folderlast',
|
'nolog',
|
||||||
'folderlast',
|
'prefix1',
|
||||||
'nomixfolders',
|
'prefix2',
|
||||||
'skipemptyfolders',
|
'sep1',
|
||||||
'include',
|
'sep2',
|
||||||
'include',
|
'nofoldersizesatend',
|
||||||
'subfolder1',
|
'justfoldersizes',
|
||||||
'subscribed',
|
'proxyauth1',
|
||||||
'subscribe',
|
'skipemptyfolders',
|
||||||
'prefix1',
|
'include',
|
||||||
'prefix2',
|
'subfolder1',
|
||||||
'sep1',
|
'subscribed',
|
||||||
'sep2',
|
'subscribe',
|
||||||
'nofoldersizesatend',
|
'debug',
|
||||||
'justfoldersizes',
|
'debugimap2',
|
||||||
'pidfile',
|
'domino1',
|
||||||
'pidfilelocking',
|
'exchange1',
|
||||||
'nolog',
|
'exchange2',
|
||||||
'logfile',
|
'justlogin',
|
||||||
'logdir',
|
'keepalive1',
|
||||||
'debugcrossduplicates',
|
'keepalive2',
|
||||||
'disarmreadreceipts',
|
'noabletosearch2',
|
||||||
'truncmess',
|
'noexpunge2',
|
||||||
'synclabels',
|
'noresyncflags',
|
||||||
'resynclabels',
|
'nossl1',
|
||||||
'resyncflags',
|
'nouidexpunge2',
|
||||||
'noresyncflags',
|
'syncinternaldates',
|
||||||
'filterbuggyflags',
|
'idatefromheader',
|
||||||
'expunge1',
|
'useuid',
|
||||||
'noexpunge1',
|
'debugflags',
|
||||||
'delete1emptyfolders',
|
'debugimap',
|
||||||
'delete2folders',
|
'delete1emptyfolders',
|
||||||
'noexpunge2',
|
'delete2folders',
|
||||||
'nouidexpunge2',
|
'gmail2',
|
||||||
'syncinternaldates',
|
'office1',
|
||||||
'idatefromheader',
|
'testslive6',
|
||||||
'maxsize',
|
'debugimap1',
|
||||||
'minsize',
|
'errorsmax',
|
||||||
'minage',
|
'tests',
|
||||||
'search',
|
'gmail1',
|
||||||
'search1',
|
'maxmessagespersecond',
|
||||||
'search2',
|
'maxbytesafter',
|
||||||
'noabletosearch',
|
'maxsleep',
|
||||||
'noabletosearch1',
|
'abort',
|
||||||
'noabletosearch2',
|
'resyncflags',
|
||||||
'maxlinelength',
|
'resynclabels',
|
||||||
'useheader',
|
'syncacls',
|
||||||
'useheader',
|
'nosyncacls',
|
||||||
'syncduplicates',
|
'nousecache',
|
||||||
'usecache',
|
'office2',
|
||||||
'nousecache',
|
'testslive',
|
||||||
'useuid',
|
'debugmemory',
|
||||||
'syncacls',
|
'exitwhenover',
|
||||||
'nosyncacls',
|
'noid',
|
||||||
'debug',
|
'noexpunge1',
|
||||||
'debugfolders',
|
'authmd51',
|
||||||
'debugcontent',
|
'logfile',
|
||||||
'debugflags',
|
'proxyauth2',
|
||||||
'debugimap1',
|
'domain1',
|
||||||
'debugimap2',
|
'domain2',
|
||||||
'debugimap',
|
'oauthaccesstoken1',
|
||||||
'debugmemory',
|
'oauthaccesstoken2',
|
||||||
'errorsmax',
|
'oauthdirect1',
|
||||||
'tests',
|
'oauthdirect2',
|
||||||
'testslive',
|
'folder',
|
||||||
'testslive6',
|
'folderrec',
|
||||||
'gmail1',
|
'folderfirst',
|
||||||
'gmail2',
|
'folderlast',
|
||||||
'office1',
|
'nomixfolders',
|
||||||
'office2',
|
'authmd52',
|
||||||
'exchange1',
|
'debugfolders',
|
||||||
'exchange2',
|
'nossl2',
|
||||||
'domino1',
|
'ssl2',
|
||||||
'domino2',
|
'tls2',
|
||||||
'keepalive1',
|
'notls2',
|
||||||
'keepalive2',
|
'debugssl',
|
||||||
'maxmessagespersecond',
|
'notls1',
|
||||||
'maxbytesafter',
|
'inet4',
|
||||||
'maxsleep',
|
'inet6',
|
||||||
'abort',
|
'log',
|
||||||
'exitwhenover',
|
'showpasswords'
|
||||||
'noid',
|
|
||||||
'justconnect',
|
|
||||||
'justlogin',
|
|
||||||
'justfolders'
|
|
||||||
),
|
),
|
||||||
'blacklist' => array(
|
'blacklist' => array(
|
||||||
'skipmess',
|
'skipmess',
|
||||||
'delete2foldersonly',
|
'delete2foldersonly',
|
||||||
'delete2foldersbutnot',
|
'delete2foldersbutnot',
|
||||||
'regexflag',
|
'regexflag',
|
||||||
'regexmess',
|
'regexmess',
|
||||||
'pipemess',
|
'pipemess',
|
||||||
'regextrans2',
|
'regextrans2',
|
||||||
'maxlinelengthcmd'
|
'maxlinelengthcmd'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
+28
-19
@@ -178,15 +178,22 @@ if (isset($_GET['query'])) {
|
|||||||
// parse post data
|
// parse post data
|
||||||
$post = trim(file_get_contents('php://input'));
|
$post = trim(file_get_contents('php://input'));
|
||||||
if ($post) $post = json_decode($post);
|
if ($post) $post = json_decode($post);
|
||||||
|
|
||||||
// decode base64 strings
|
|
||||||
$clientDataJSON = base64_decode($post->clientDataJSON);
|
|
||||||
$attestationObject = base64_decode($post->attestationObject);
|
|
||||||
|
|
||||||
// process registration data from authenticator
|
// process registration data from authenticator
|
||||||
try {
|
try {
|
||||||
|
// decode base64 strings
|
||||||
|
$clientDataJSON = base64_decode($post->clientDataJSON);
|
||||||
|
$attestationObject = base64_decode($post->attestationObject);
|
||||||
|
|
||||||
// processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true, $failIfRootMismatch=true)
|
// processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true, $failIfRootMismatch=true)
|
||||||
$data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $_SESSION['challenge'], false, true);
|
$data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $_SESSION['challenge'], false, true);
|
||||||
|
|
||||||
|
// safe authenticator in mysql `tfa` table
|
||||||
|
$_data['tfa_method'] = $post->tfa_method;
|
||||||
|
$_data['key_id'] = $post->key_id;
|
||||||
|
$_data['confirm_password'] = $post->confirm_password;
|
||||||
|
$_data['registration'] = $data;
|
||||||
|
set_tfa($_data);
|
||||||
}
|
}
|
||||||
catch (Throwable $ex) {
|
catch (Throwable $ex) {
|
||||||
// err
|
// err
|
||||||
@@ -197,11 +204,6 @@ if (isset($_GET['query'])) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// safe authenticator in mysql `tfa` table
|
|
||||||
$_data['tfa_method'] = $post->tfa_method;
|
|
||||||
$_data['key_id'] = $post->key_id;
|
|
||||||
$_data['registration'] = $data;
|
|
||||||
set_tfa($_data);
|
|
||||||
|
|
||||||
// send response
|
// send response
|
||||||
$return = new stdClass();
|
$return = new stdClass();
|
||||||
@@ -419,7 +421,7 @@ if (isset($_GET['query'])) {
|
|||||||
// }
|
// }
|
||||||
$ids = NULL;
|
$ids = NULL;
|
||||||
|
|
||||||
$getArgs = $WebAuthn->getGetArgs($ids, 30, true, true, true, true, $GLOBALS['FIDO2_UV_FLAG_LOGIN']);
|
$getArgs = $WebAuthn->getGetArgs($ids, 30, false, false, false, false, $GLOBALS['FIDO2_UV_FLAG_LOGIN']);
|
||||||
print(json_encode($getArgs));
|
print(json_encode($getArgs));
|
||||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||||
return;
|
return;
|
||||||
@@ -428,8 +430,11 @@ if (isset($_GET['query'])) {
|
|||||||
case "webauthn-tfa-registration":
|
case "webauthn-tfa-registration":
|
||||||
if (isset($_SESSION["mailcow_cc_role"])) {
|
if (isset($_SESSION["mailcow_cc_role"])) {
|
||||||
// Exclude existing CredentialIds, if any
|
// Exclude existing CredentialIds, if any
|
||||||
$stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username");
|
$stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username AND authmech = :authmech");
|
||||||
$stmt->execute(array(':username' => $_SESSION['mailcow_cc_username']));
|
$stmt->execute(array(
|
||||||
|
':username' => $_SESSION['mailcow_cc_username'],
|
||||||
|
':authmech' => 'webauthn'
|
||||||
|
));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
while($row = array_shift($rows)) {
|
while($row = array_shift($rows)) {
|
||||||
$excludeCredentialIds[] = base64_decode($row['keyHandle']);
|
$excludeCredentialIds[] = base64_decode($row['keyHandle']);
|
||||||
@@ -450,20 +455,24 @@ if (isset($_GET['query'])) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "webauthn-tfa-get-args":
|
case "webauthn-tfa-get-args":
|
||||||
$stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username");
|
$stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username AND authmech = :authmech");
|
||||||
$stmt->execute(array(':username' => $_SESSION['pending_mailcow_cc_username']));
|
$stmt->execute(array(
|
||||||
|
':username' => $_SESSION['pending_mailcow_cc_username'],
|
||||||
|
':authmech' => 'webauthn'
|
||||||
|
));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
while($row = array_shift($rows)) {
|
if (count($rows) == 0) {
|
||||||
$cids[] = base64_decode($row['keyHandle']);
|
|
||||||
}
|
|
||||||
if (count($cids) == 0) {
|
|
||||||
print(json_encode(array(
|
print(json_encode(array(
|
||||||
'type' => 'error',
|
'type' => 'error',
|
||||||
'msg' => 'Cannot find matching credentialIds'
|
'msg' => 'Cannot find matching credentialIds'
|
||||||
)));
|
)));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$cids[] = base64_decode($row['keyHandle']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$getArgs = $WebAuthn->getGetArgs($cids, 30, true, true, true, true, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN']);
|
$getArgs = $WebAuthn->getGetArgs($cids, 30, false, false, false, false, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN']);
|
||||||
$getArgs->publicKey->extensions = array('appid' => "https://".$getArgs->publicKey->rpId);
|
$getArgs->publicKey->extensions = array('appid' => "https://".$getArgs->publicKey->rpId);
|
||||||
print(json_encode($getArgs));
|
print(json_encode($getArgs));
|
||||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||||
|
|||||||
@@ -839,7 +839,7 @@
|
|||||||
"confirm_delete": "Potvrdit smazání prvku.",
|
"confirm_delete": "Potvrdit smazání prvku.",
|
||||||
"danger": "Nebezpečí",
|
"danger": "Nebezpečí",
|
||||||
"deliver_inbox": "Doručit do schránky",
|
"deliver_inbox": "Doručit do schránky",
|
||||||
"disabled_by_config": "Funkce karanténa je momentálně vypnuta v nastavení systému.",
|
"disabled_by_config": "Funkce karanténa je momentálně vypnuta v nastavení systému. Nastavte, prosím, prvkům karantény hodnoty \"počet zadržených zpráv\" a \"maximální velikost\".",
|
||||||
"download_eml": "Stáhnout (.eml)",
|
"download_eml": "Stáhnout (.eml)",
|
||||||
"empty": "Žádné výsledky",
|
"empty": "Žádné výsledky",
|
||||||
"high_danger": "Vysoké nebezpečí",
|
"high_danger": "Vysoké nebezpečí",
|
||||||
@@ -265,8 +265,8 @@
|
|||||||
"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",
|
||||||
"quota_notifications": "Quota Benachrichtigungen",
|
"quota_notifications": "Quota-Benachrichtigungen",
|
||||||
"quota_notifications_info": "Quota Benachrichtigungen werden an Mailboxen versendet, die 80 respektive 95 Prozent der zur Verfügung stehenden Quota überschreiten.",
|
"quota_notifications_info": "Quota-Benachrichtigungen werden an Mailboxen versendet, die 80 respektive 95 Prozent der zur Verfügung stehenden Quota überschreiten.",
|
||||||
"quota_notifications_vars": "{{percent}} entspricht der aktuellen Quota in Prozent<br>{{username}} entspricht dem Mailbox-Namen",
|
"quota_notifications_vars": "{{percent}} entspricht der aktuellen Quota in Prozent<br>{{username}} entspricht dem Mailbox-Namen",
|
||||||
"r_active": "Aktive Restriktionen",
|
"r_active": "Aktive Restriktionen",
|
||||||
"r_inactive": "Inaktive Restriktionen",
|
"r_inactive": "Inaktive Restriktionen",
|
||||||
@@ -574,6 +574,7 @@
|
|||||||
"pushover_sender_regex": "Consider the following sender regex",
|
"pushover_sender_regex": "Consider the following sender regex",
|
||||||
"pushover_text": "Notification text",
|
"pushover_text": "Notification text",
|
||||||
"pushover_title": "Notification title",
|
"pushover_title": "Notification title",
|
||||||
|
"pushover_sound": "Sound",
|
||||||
"pushover_vars": "When no sender filter is defined, all mails will be considered.<br>Regex filters as well as exact sender checks can be defined individually and will be considered sequentially. They do not depend on each other.<br>Useable variables for text and title (please take note of data protection policies)",
|
"pushover_vars": "When no sender filter is defined, all mails will be considered.<br>Regex filters as well as exact sender checks can be defined individually and will be considered sequentially. They do not depend on each other.<br>Useable variables for text and title (please take note of data protection policies)",
|
||||||
"pushover_verify": "Verify credentials",
|
"pushover_verify": "Verify credentials",
|
||||||
"quota_mb": "Quota (MiB)",
|
"quota_mb": "Quota (MiB)",
|
||||||
@@ -1097,6 +1098,7 @@
|
|||||||
"pushover_sender_regex": "Match senders by the following regex",
|
"pushover_sender_regex": "Match senders by the following regex",
|
||||||
"pushover_text": "Notification text",
|
"pushover_text": "Notification text",
|
||||||
"pushover_title": "Notification title",
|
"pushover_title": "Notification title",
|
||||||
|
"pushover_sound": "Sound",
|
||||||
"pushover_vars": "When no sender filter is defined, all mails will be considered.<br>Regex filters as well as exact sender checks can be defined individually and will be considered sequentially. They do not depend on each other.<br>Useable variables for text and title (please take note of data protection policies)",
|
"pushover_vars": "When no sender filter is defined, all mails will be considered.<br>Regex filters as well as exact sender checks can be defined individually and will be considered sequentially. They do not depend on each other.<br>Useable variables for text and title (please take note of data protection policies)",
|
||||||
"pushover_verify": "Verify credentials",
|
"pushover_verify": "Verify credentials",
|
||||||
"q_add_header": "Junk folder",
|
"q_add_header": "Junk folder",
|
||||||
@@ -102,7 +102,8 @@
|
|||||||
"timeout2": "Délai d'expiration pour la connexion à l'hôte local",
|
"timeout2": "Délai d'expiration pour la connexion à l'hôte local",
|
||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"validate": "Valider",
|
"validate": "Valider",
|
||||||
"validation_success": "Validation réussie"
|
"validation_success": "Validation réussie",
|
||||||
|
"bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici."
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Accès",
|
"access": "Accès",
|
||||||
@@ -322,7 +323,9 @@
|
|||||||
"yes": "✓",
|
"yes": "✓",
|
||||||
"api_read_write": "Accès Lecture-Écriture",
|
"api_read_write": "Accès Lecture-Écriture",
|
||||||
"oauth2_add_client": "Ajouter un client OAuth2",
|
"oauth2_add_client": "Ajouter un client OAuth2",
|
||||||
"password_policy": "Politique de mots de passe"
|
"password_policy": "Politique de mots de passe",
|
||||||
|
"admins": "Administrateurs",
|
||||||
|
"api_read_only": "Accès lecture-seule"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Accès refusé ou données de formulaire non valides",
|
"access_denied": "Accès refusé ou données de formulaire non valides",
|
||||||
@@ -973,7 +973,8 @@
|
|||||||
"verified_fido2_login": "Verified FIDO2 login",
|
"verified_fido2_login": "Verified FIDO2 login",
|
||||||
"verified_totp_login": "Verified TOTP login",
|
"verified_totp_login": "Verified TOTP login",
|
||||||
"verified_webauthn_login": "Verified WebAuthn login",
|
"verified_webauthn_login": "Verified WebAuthn login",
|
||||||
"verified_yotp_login": "Verified Yubico OTP login"
|
"verified_yotp_login": "Verified Yubico OTP login",
|
||||||
|
"domain_add_dkim_available": "Esisteva già una chiave DKIM"
|
||||||
},
|
},
|
||||||
"tfa": {
|
"tfa": {
|
||||||
"api_register": "%s usa le API Yubico Cloud. Richiedi una chiave API <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">qui</a>",
|
"api_register": "%s usa le API Yubico Cloud. Richiedi una chiave API <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">qui</a>",
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user