Compare commits
108 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 | ||
|
623397d20a | ||
|
7d46de33d8 | ||
|
f34d3620b1 | ||
|
70e99447f9 | ||
|
7b57b3392c | ||
|
492451bfee | ||
|
a1e8077f45 | ||
|
956e4e2aa7 | ||
|
c2e0a275e1 | ||
|
514079fe96 | ||
|
27e9210d52 | ||
|
1b6e5b7116 | ||
|
0dab215e27 | ||
|
6ec136e63f | ||
|
bba5671eaf | ||
|
88d7593d89 | ||
|
bd23b80d45 | ||
|
f96e0c4071 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1 +1 @@
|
|||||||
custom: https://mailcow.github.io/mailcow-dockerized-docs/#help-mailcow
|
custom: ["https://www.servercow.de/mailcow?lang=en#sal"]
|
||||||
|
162
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
162
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
@@ -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
|
||||||
|
13
.github/ISSUE_TEMPLATE/pr_to_nighty_template.yml
vendored
Normal file
13
.github/ISSUE_TEMPLATE/pr_to_nighty_template.yml
vendored
Normal file
@@ -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 -->
|
BIN
.github/workflows/assets/check_prs_if_on_staging.png
vendored
Normal file
BIN
.github/workflows/assets/check_prs_if_on_staging.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
33
.github/workflows/check_prs_if_on_staging.yml
vendored
Normal file
33
.github/workflows/check_prs_if_on_staging.yml
vendored
Normal file
@@ -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@v6.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
|
||||||
|
25
.github/workflows/pr_to_nightly.yml
vendored
Normal file
25
.github/workflows/pr_to_nightly.yml
vendored
Normal file
@@ -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
|
34
.github/workflows/rebuild_backup_image.yml
vendored
Normal file
34
.github/workflows/rebuild_backup_image.yml
vendored
Normal file
@@ -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
|
@@ -4,9 +4,12 @@ on:
|
|||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
tweet:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
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
|
- name: Tweet-trigger-publish-release
|
||||||
uses: mugi111/tweet-trigger-release@v1.1
|
uses: mugi111/tweet-trigger-release@v1.1
|
||||||
with:
|
with:
|
||||||
@@ -14,4 +17,4 @@ jobs:
|
|||||||
consumer_secret: ${{ secrets.CONSUMER_SECRET }}
|
consumer_secret: ${{ secrets.CONSUMER_SECRET }}
|
||||||
access_token_key: ${{ secrets.ACCESS_TOKEN_KEY }}
|
access_token_key: ${{ secrets.ACCESS_TOKEN_KEY }}
|
||||||
access_token_secret: ${{ secrets.ACCESS_TOKEN_SECRET }}
|
access_token_secret: ${{ secrets.ACCESS_TOKEN_SECRET }}
|
||||||
tweet_body: 'A new mailcow-dockerized Release has been Released on GitHub! Checkout our GitHub Page for the latest Release: github.com/mailcow/mailcow-dockerized/releases/latest'
|
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'
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
||||||
|
|
||||||
## We stand with 🇺🇦
|
|
||||||
|
|
||||||
[](https://github.com/mailcow/mailcow-dockerized/actions/workflows/integration_tests.yml)
|
[](https://github.com/mailcow/mailcow-dockerized/actions/workflows/integration_tests.yml)
|
||||||
[](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
[](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
||||||
[](https://twitter.com/mailcow_email)
|
[](https://twitter.com/mailcow_email)
|
||||||
@@ -36,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é)
|
3
data/Dockerfiles/backup/Dockerfile
Normal file
3
data/Dockerfiles/backup/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
FROM debian:bullseye-slim
|
||||||
|
|
||||||
|
RUN apt update && apt install pigz
|
@@ -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 = ?");
|
||||||
|
@@ -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,15 +358,22 @@ 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(),
|
||||||
|
new_rule.get_dst() == rule.get_dst(),
|
||||||
|
new_rule.target.parameters == rule.target.parameters,
|
||||||
|
new_rule.target.name == rule.target.name
|
||||||
|
))
|
||||||
|
if position == 0:
|
||||||
|
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:
|
else:
|
||||||
for position, item in enumerate(chain.rules):
|
if match:
|
||||||
if item == get_snat4_rule():
|
logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
|
||||||
if position != 0:
|
chain.delete_rule(rule)
|
||||||
chain.delete_rule(get_snat4_rule())
|
|
||||||
table.commit()
|
table.commit()
|
||||||
table.autocommit = True
|
table.autocommit = True
|
||||||
except:
|
except:
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
@@ -3349,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: ""
|
||||||
@@ -3392,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: ""
|
||||||
@@ -3413,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
|
||||||
@@ -5501,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
|
||||||
|
2
data/web/css/build/007-languages.min.css
vendored
2
data/web/css/build/007-languages.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -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'])) {
|
||||||
|
@@ -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 = "25072022_2300";
|
$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));
|
||||||
@@ -1264,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;");
|
||||||
|
@@ -195,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 {
|
||||||
@@ -215,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)) {
|
||||||
|
@@ -76,33 +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)',
|
||||||
'tr' => 'Türkçe (Turkish)',
|
'sv-se' => 'Svenska (Swedish)',
|
||||||
'uk' => 'Українська (Ukrainian)',
|
'tr-tr' => 'Türkçe (Turkish)',
|
||||||
'zh' => '中文 (Chinese)'
|
'uk-ua' => 'Українська (Ukrainian)',
|
||||||
|
'zh-cn' => '简体中文 (Simplified Chinese)',
|
||||||
|
'zh-tw' => '繁體中文 (Traditional Chinese)',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Change theme (default: lumen)
|
// Change theme (default: lumen)
|
||||||
|
@@ -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",
|
@@ -536,6 +536,7 @@
|
|||||||
"pushover_sender_regex": "Uitsluitend een afzender met de volgende regex",
|
"pushover_sender_regex": "Uitsluitend een afzender met de volgende regex",
|
||||||
"pushover_text": "Meldingstekst ({SUBJECT} zal worden vervangen door het onderwerp)",
|
"pushover_text": "Meldingstekst ({SUBJECT} zal worden vervangen door het onderwerp)",
|
||||||
"pushover_title": "Meldingstitel",
|
"pushover_title": "Meldingstitel",
|
||||||
|
"pushover_sound": "Geluid",
|
||||||
"pushover_vars": "Wanneer er geen afzenders zijn uitgesloten zullen alle mails doorkomen.<br>Regex-filters en afzendercontroles kunnen individueel worden ingesteld en zullen in volgorde worden verwerkt. Ze zijn niet afhankelijk van elkaar.<br>Bruikbare variabelen voor tekst en titel (neem het gegevensbeschermingsbeleid in acht)",
|
"pushover_vars": "Wanneer er geen afzenders zijn uitgesloten zullen alle mails doorkomen.<br>Regex-filters en afzendercontroles kunnen individueel worden ingesteld en zullen in volgorde worden verwerkt. Ze zijn niet afhankelijk van elkaar.<br>Bruikbare variabelen voor tekst en titel (neem het gegevensbeschermingsbeleid in acht)",
|
||||||
"pushover_verify": "Verifieer aanmeldingsgegevens",
|
"pushover_verify": "Verifieer aanmeldingsgegevens",
|
||||||
"quota_mb": "Quota (MiB)",
|
"quota_mb": "Quota (MiB)",
|
||||||
@@ -1002,6 +1003,7 @@
|
|||||||
"pushover_sender_regex": "Uitsluitend een afzender met de volgende regex",
|
"pushover_sender_regex": "Uitsluitend een afzender met de volgende regex",
|
||||||
"pushover_text": "Meldingstekst ({SUBJECT} zal worden vervangen door het onderwerp)",
|
"pushover_text": "Meldingstekst ({SUBJECT} zal worden vervangen door het onderwerp)",
|
||||||
"pushover_title": "Meldingstitel",
|
"pushover_title": "Meldingstitel",
|
||||||
|
"pushover_sound": "Geluid",
|
||||||
"pushover_vars": "Wanneer er geen afzenders zijn uitgesloten zullen alle mails doorkomen.<br>Regex-filters en afzendercontroles kunnen individueel worden ingesteld en zullen in volgorde worden verwerkt. Ze zijn niet afhankelijk van elkaar.<br>Bruikbare variabelen voor tekst en titel (let op het gegevensbeschermingsbeleid)",
|
"pushover_vars": "Wanneer er geen afzenders zijn uitgesloten zullen alle mails doorkomen.<br>Regex-filters en afzendercontroles kunnen individueel worden ingesteld en zullen in volgorde worden verwerkt. Ze zijn niet afhankelijk van elkaar.<br>Bruikbare variabelen voor tekst en titel (let op het gegevensbeschermingsbeleid)",
|
||||||
"pushover_verify": "Verifieer aanmeldingsgegevens",
|
"pushover_verify": "Verifieer aanmeldingsgegevens",
|
||||||
"q_add_header": "Spamfolder",
|
"q_add_header": "Spamfolder",
|
@@ -106,7 +106,7 @@
|
|||||||
"username": "Používateľské meno",
|
"username": "Používateľské meno",
|
||||||
"validate": "Overiť",
|
"validate": "Overiť",
|
||||||
"validation_success": "Úspešne overené",
|
"validation_success": "Úspešne overené",
|
||||||
"app_passwd_protocols": "Povolené protokoly"
|
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Prístup",
|
"access": "Prístup",
|
1187
data/web/lang/lang.zh-cn.json
Normal file
1187
data/web/lang/lang.zh-cn.json
Normal file
File diff suppressed because it is too large
Load Diff
1187
data/web/lang/lang.zh-tw.json
Normal file
1187
data/web/lang/lang.zh-tw.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -32,12 +32,12 @@
|
|||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
{% if mailcow_locale %}
|
{% if mailcow_locale %}
|
||||||
<li class="dropdown{% if available_languages|length == 1 %}lang-link-disabled{% endif %}">
|
<li class="dropdown{% if available_languages|length == 1 %}lang-link-disabled{% endif %}">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="flag-icon flag-icon-{{ mailcow_locale }}"></span><span class="caret"></span></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span><span class="caret"></span></a>
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu" role="menu">
|
||||||
{% for key, val in available_languages %}
|
{% for key, val in available_languages %}
|
||||||
<li{% if mailcow_locale == key %} class="active"{% endif %}>
|
<li{% if mailcow_locale == key %} class="active"{% endif %}>
|
||||||
<a href="?{{ query_string({'lang': key}) }}">
|
<a href="?{{ query_string({'lang': key}) }}">
|
||||||
<span class="flag-icon flag-icon-{{ key }}"></span>{{ val }}
|
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -172,7 +172,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||||||
// TFA, CSRF, Alerts in footer.inc.php
|
// TFA, CSRF, Alerts in footer.inc.php
|
||||||
// Other general functions in mailcow.js
|
// Other general functions in mailcow.js
|
||||||
{% for alert_type, alert_msg in alerts %}
|
{% for alert_type, alert_msg in alerts %}
|
||||||
mailcow_alert_box('{{ alert_msg|raw }}', '{{ alert_type }}');
|
mailcow_alert_box('{{ alert_msg|raw|e("js") }}', '{{ alert_type }}');
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
// Confirm TFA modal
|
// Confirm TFA modal
|
||||||
|
@@ -275,7 +275,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<p class="help-block">{{ lang.user.pushover_info|format(mailbox)|raw }}</p>
|
<p class="help-block">{{ lang.user.pushover_info|format(mailbox)|raw }}</p>
|
||||||
<p class="help-block">{{ lang.edit.pushover_vars|raw }}: <code>{SUBJECT}</code>, <code>{SENDER}</code></p>
|
<p class="help-block">{{ lang.edit.pushover_vars|raw }}: <code>{SUBJECT}</code>, <code>{SENDER}</code>, <code>{SENDER_ADDRESS}</code>, <code>{SENDER_NAME}</code>, <code>{TO_NAME}</code>, <code>{TO_ADDRESS}</code></p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
@@ -308,6 +308,36 @@
|
|||||||
<input type="text" class="form-control" name="senders" value="{{ pushover_data.senders }}" placeholder="sender1@example.com, sender2@example.com">
|
<input type="text" class="form-control" name="senders" value="{{ pushover_data.senders }}" placeholder="sender1@example.com, sender2@example.com">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sound">{{ lang.edit.pushover_sound }}</label><br>
|
||||||
|
<select name="sound" class="form-control">
|
||||||
|
<option value="pushover"{% if pushover_data.attributes.sound == 'pushover' %} selected{% endif %}>Pushover (default)</option>
|
||||||
|
<option value="bike"{% if pushover_data.attributes.sound == 'bike' %} selected{% endif %}>Bike</option>
|
||||||
|
<option value="bugle"{% if pushover_data.attributes.sound == 'bugle' %} selected{% endif %}>Bugle</option>
|
||||||
|
<option value="cashregister"{% if pushover_data.attributes.sound == 'cashregister' %} selected{% endif %}>Cash Register</option>
|
||||||
|
<option value="classical"{% if pushover_data.attributes.sound == 'classical' %} selected{% endif %}>Classical</option>
|
||||||
|
<option value="cosmic"{% if pushover_data.attributes.sound == 'cosmic' %} selected{% endif %}>Cosmic</option>
|
||||||
|
<option value="falling"{% if pushover_data.attributes.sound == 'falling' %} selected{% endif %}>Falling</option>
|
||||||
|
<option value="gamelan"{% if pushover_data.attributes.sound == 'gamelan' %} selected{% endif %}>Gamelan</option>
|
||||||
|
<option value="incoming"{% if pushover_data.attributes.sound == 'incoming' %} selected{% endif %}>Incoming</option>
|
||||||
|
<option value="intermission"{% if pushover_data.attributes.sound == 'intermission' %} selected{% endif %}>Intermission</option>
|
||||||
|
<option value="magic"{% if pushover_data.attributes.sound == 'magic' %} selected{% endif %}>Magic</option>
|
||||||
|
<option value="mechanical"{% if pushover_data.attributes.sound == 'mechanical' %} selected{% endif %}>Mechanical</option>
|
||||||
|
<option value="pianobar"{% if pushover_data.attributes.sound == 'pianobar' %} selected{% endif %}>Piano Bar</option>
|
||||||
|
<option value="siren"{% if pushover_data.attributes.sound == 'siren' %} selected{% endif %}>Siren</option>
|
||||||
|
<option value="spacealarm"{% if pushover_data.attributes.sound == 'spacealarm' %} selected{% endif %}>Space Alarm</option>
|
||||||
|
<option value="tugboat"{% if pushover_data.attributes.sound == 'tugboat' %} selected{% endif %}>Tug Boat</option>
|
||||||
|
<option value="alien"{% if pushover_data.attributes.sound == 'alien' %} selected{% endif %}>Alien Alarm (long)</option>
|
||||||
|
<option value="climb"{% if pushover_data.attributes.sound == 'climb' %} selected{% endif %}>Climb (long)</option>
|
||||||
|
<option value="persistent"{% if pushover_data.attributes.sound == 'persistent' %} selected{% endif %}>Persistent (long)</option>
|
||||||
|
<option value="echo"{% if pushover_data.attributes.sound == 'echo' %} selected{% endif %}>Pushover Echo (long)</option>
|
||||||
|
<option value="updown"{% if pushover_data.attributes.sound == 'updown' %} selected{% endif %}>Up Down (long)</option>
|
||||||
|
<option value="vibrate"{% if pushover_data.attributes.sound == 'vibrate' %} selected{% endif %}>Vibrate Only</option>
|
||||||
|
<option value="none"{% if pushover_data.attributes.sound == 'none' %} selected{% endif %}> None (silent) </option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label><input type="checkbox" value="1" name="active"{% if pushover_data.active == '1' %} checked{% endif %}> {{ lang.edit.active }}</label>
|
<label><input type="checkbox" value="1" name="active"{% if pushover_data.active == '1' %} checked{% endif %}> {{ lang.edit.active }}</label>
|
||||||
|
@@ -45,13 +45,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{% if not oauth2_request %}
|
{% if not oauth2_request %}
|
||||||
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-xs-lg btn-default pull-right dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-xs-lg btn-default pull-right dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<span class="flag-icon flag-icon-{{ mailcow_locale }}"></span> <span class="caret"></span>
|
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span> <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu pull-right login">
|
<ul class="dropdown-menu pull-right login">
|
||||||
{% for key, val in available_languages %}
|
{% for key, val in available_languages %}
|
||||||
<li{% if mailcow_locale == key %} class="active"{% endif %}>
|
<li{% if mailcow_locale == key %} class="active"{% endif %}>
|
||||||
<a href="?{{ query_string({'lang': key}) }}">
|
<a href="?{{ query_string({'lang': key}) }}">
|
||||||
<span class="flag-icon flag-icon-{{ key }}"></span>{{ val }}
|
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<p class="help-block">{{ lang.user.pushover_info|format(mailcow_cc_username)|raw }}</p>
|
<p class="help-block">{{ lang.user.pushover_info|format(mailcow_cc_username)|raw }}</p>
|
||||||
<p class="help-block">{{ lang.user.pushover_vars|raw }}: <code>{SUBJECT}</code>, <code>{SENDER}</code></p>
|
<p class="help-block">{{ lang.edit.pushover_vars|raw }}: <code>{SUBJECT}</code>, <code>{SENDER}</code>, <code>{SENDER_ADDRESS}</code>, <code>{SENDER_NAME}</code>, <code>{TO_NAME}</code>, <code>{TO_ADDRESS}</code></p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
@@ -42,6 +42,36 @@
|
|||||||
<input type="text" class="form-control" name="senders" value="{{ pushover_data.senders }}" placeholder="sender1@example.com, sender2@example.com">
|
<input type="text" class="form-control" name="senders" value="{{ pushover_data.senders }}" placeholder="sender1@example.com, sender2@example.com">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sound">{{ lang.edit.pushover_sound }}</label><br>
|
||||||
|
<select name="sound" class="form-control">
|
||||||
|
<option value="pushover"{% if pushover_data.attributes.sound == 'pushover' %} selected{% endif %}>Pushover (default)</option>
|
||||||
|
<option value="bike"{% if pushover_data.attributes.sound == 'bike' %} selected{% endif %}>Bike</option>
|
||||||
|
<option value="bugle"{% if pushover_data.attributes.sound == 'bugle' %} selected{% endif %}>Bugle</option>
|
||||||
|
<option value="cashregister"{% if pushover_data.attributes.sound == 'cashregister' %} selected{% endif %}>Cash Register</option>
|
||||||
|
<option value="classical"{% if pushover_data.attributes.sound == 'classical' %} selected{% endif %}>Classical</option>
|
||||||
|
<option value="cosmic"{% if pushover_data.attributes.sound == 'cosmic' %} selected{% endif %}>Cosmic</option>
|
||||||
|
<option value="falling"{% if pushover_data.attributes.sound == 'falling' %} selected{% endif %}>Falling</option>
|
||||||
|
<option value="gamelan"{% if pushover_data.attributes.sound == 'gamelan' %} selected{% endif %}>Gamelan</option>
|
||||||
|
<option value="incoming"{% if pushover_data.attributes.sound == 'incoming' %} selected{% endif %}>Incoming</option>
|
||||||
|
<option value="intermission"{% if pushover_data.attributes.sound == 'intermission' %} selected{% endif %}>Intermission</option>
|
||||||
|
<option value="magic"{% if pushover_data.attributes.sound == 'magic' %} selected{% endif %}>Magic</option>
|
||||||
|
<option value="mechanical"{% if pushover_data.attributes.sound == 'mechanical' %} selected{% endif %}>Mechanical</option>
|
||||||
|
<option value="pianobar"{% if pushover_data.attributes.sound == 'pianobar' %} selected{% endif %}>Piano Bar</option>
|
||||||
|
<option value="siren"{% if pushover_data.attributes.sound == 'siren' %} selected{% endif %}>Siren</option>
|
||||||
|
<option value="spacealarm"{% if pushover_data.attributes.sound == 'spacealarm' %} selected{% endif %}>Space Alarm</option>
|
||||||
|
<option value="tugboat"{% if pushover_data.attributes.sound == 'tugboat' %} selected{% endif %}>Tug Boat</option>
|
||||||
|
<option value="alien"{% if pushover_data.attributes.sound == 'alien' %} selected{% endif %}>Alien Alarm (long)</option>
|
||||||
|
<option value="climb"{% if pushover_data.attributes.sound == 'climb' %} selected{% endif %}>Climb (long)</option>
|
||||||
|
<option value="persistent"{% if pushover_data.attributes.sound == 'persistent' %} selected{% endif %}>Persistent (long)</option>
|
||||||
|
<option value="echo"{% if pushover_data.attributes.sound == 'echo' %} selected{% endif %}>Pushover Echo (long)</option>
|
||||||
|
<option value="updown"{% if pushover_data.attributes.sound == 'updown' %} selected{% endif %}>Up Down (long)</option>
|
||||||
|
<option value="vibrate"{% if pushover_data.attributes.sound == 'vibrate' %} selected{% endif %}>Vibrate Only</option>
|
||||||
|
<option value="none"{% if pushover_data.attributes.sound == 'none' %} selected{% endif %}> None (silent) </option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label><input type="checkbox" value="1" name="active"{% if pushover_data.active == '1' %} checked{% endif %}> {{ lang.user.active }}</label>
|
<label><input type="checkbox" value="1" name="active"{% if pushover_data.active == '1' %} checked{% endif %}> {{ lang.user.active }}</label>
|
||||||
|
@@ -41,7 +41,7 @@ services:
|
|||||||
- mysql
|
- mysql
|
||||||
|
|
||||||
redis-mailcow:
|
redis-mailcow:
|
||||||
image: redis:6-alpine
|
image: redis:7-alpine
|
||||||
volumes:
|
volumes:
|
||||||
- redis-vol-1:/data/
|
- redis-vol-1:/data/
|
||||||
restart: always
|
restart: always
|
||||||
@@ -76,7 +76,7 @@ services:
|
|||||||
- clamd
|
- clamd
|
||||||
|
|
||||||
rspamd-mailcow:
|
rspamd-mailcow:
|
||||||
image: mailcow/rspamd:1.90
|
image: mailcow/rspamd:1.91
|
||||||
stop_grace_period: 30s
|
stop_grace_period: 30s
|
||||||
depends_on:
|
depends_on:
|
||||||
- dovecot-mailcow
|
- dovecot-mailcow
|
||||||
@@ -106,7 +106,7 @@ services:
|
|||||||
- rspamd
|
- rspamd
|
||||||
|
|
||||||
php-fpm-mailcow:
|
php-fpm-mailcow:
|
||||||
image: mailcow/phpfpm:1.79
|
image: mailcow/phpfpm:1.80
|
||||||
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis-mailcow
|
- redis-mailcow
|
||||||
@@ -168,7 +168,7 @@ services:
|
|||||||
- phpfpm
|
- phpfpm
|
||||||
|
|
||||||
sogo-mailcow:
|
sogo-mailcow:
|
||||||
image: mailcow/sogo:1.111
|
image: mailcow/sogo:1.113
|
||||||
environment:
|
environment:
|
||||||
- DBNAME=${DBNAME}
|
- DBNAME=${DBNAME}
|
||||||
- DBUSER=${DBUSER}
|
- DBUSER=${DBUSER}
|
||||||
@@ -215,7 +215,7 @@ services:
|
|||||||
- sogo
|
- sogo
|
||||||
|
|
||||||
dovecot-mailcow:
|
dovecot-mailcow:
|
||||||
image: mailcow/dovecot:1.20
|
image: mailcow/dovecot:1.21
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql-mailcow
|
- mysql-mailcow
|
||||||
dns:
|
dns:
|
||||||
@@ -424,7 +424,7 @@ services:
|
|||||||
- acme
|
- acme
|
||||||
|
|
||||||
netfilter-mailcow:
|
netfilter-mailcow:
|
||||||
image: mailcow/netfilter:1.48
|
image: mailcow/netfilter:1.49
|
||||||
stop_grace_period: 30s
|
stop_grace_period: 30s
|
||||||
depends_on:
|
depends_on:
|
||||||
- dovecot-mailcow
|
- dovecot-mailcow
|
||||||
|
@@ -26,7 +26,7 @@ if(empty($targetLang)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load master lang
|
// load master lang
|
||||||
$masterLang = file_get_contents(__DIR__.'/../data/web/lang/lang.en.json');
|
$masterLang = file_get_contents(__DIR__.'/../data/web/lang/lang.en-gb.json');
|
||||||
$masterLang = json_decode($masterLang, true);
|
$masterLang = json_decode($masterLang, true);
|
||||||
|
|
||||||
// load target lang
|
// load target lang
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
DEBIAN_DOCKER_IMAGE="debian:bullseye-slim"
|
DEBIAN_DOCKER_IMAGE="mailcow/backup:latest"
|
||||||
|
|
||||||
if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then
|
if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then
|
||||||
BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}"
|
BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}"
|
||||||
@@ -52,6 +52,15 @@ BACKUP_LOCATION=$(echo ${BACKUP_LOCATION} | sed 's#/$##')
|
|||||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
COMPOSE_FILE=${SCRIPT_DIR}/../docker-compose.yml
|
COMPOSE_FILE=${SCRIPT_DIR}/../docker-compose.yml
|
||||||
ENV_FILE=${SCRIPT_DIR}/../.env
|
ENV_FILE=${SCRIPT_DIR}/../.env
|
||||||
|
THREADS=$(echo ${THREADS:-1})
|
||||||
|
|
||||||
|
if ! [[ "${THREADS}" =~ ^[1-9]+$ ]] ; then
|
||||||
|
echo "Thread input is not a number!"
|
||||||
|
exit 1
|
||||||
|
elif [[ "${THREADS}" =~ ^[1-9]+$ ]] ; then
|
||||||
|
echo "Using ${THREADS} Thread(s) for this run."
|
||||||
|
echo "Notice: You can set the Thread count with the THREADS Variable before you run this script."
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f ${COMPOSE_FILE} ]; then
|
if [ ! -f ${COMPOSE_FILE} ]; then
|
||||||
echo "Compose file not found"
|
echo "Compose file not found"
|
||||||
@@ -99,32 +108,32 @@ function backup() {
|
|||||||
docker run --name mailcow-backup --rm \
|
docker run --name mailcow-backup --rm \
|
||||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:ro,z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:ro,z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_vmail.tar.gz /vmail
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_vmail.tar.gz /vmail
|
||||||
;;&
|
;;&
|
||||||
crypt|all)
|
crypt|all)
|
||||||
docker run --name mailcow-backup --rm \
|
docker run --name mailcow-backup --rm \
|
||||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:ro,z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:ro,z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_crypt.tar.gz /crypt
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_crypt.tar.gz /crypt
|
||||||
;;&
|
;;&
|
||||||
redis|all)
|
redis|all)
|
||||||
docker exec $(docker ps -qf name=redis-mailcow) redis-cli save
|
docker exec $(docker ps -qf name=redis-mailcow) redis-cli save
|
||||||
docker run --name mailcow-backup --rm \
|
docker run --name mailcow-backup --rm \
|
||||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:ro,z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:ro,z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_redis.tar.gz /redis
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_redis.tar.gz /redis
|
||||||
;;&
|
;;&
|
||||||
rspamd|all)
|
rspamd|all)
|
||||||
docker run --name mailcow-backup --rm \
|
docker run --name mailcow-backup --rm \
|
||||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:ro,z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:ro,z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_rspamd.tar.gz /rspamd
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_rspamd.tar.gz /rspamd
|
||||||
;;&
|
;;&
|
||||||
postfix|all)
|
postfix|all)
|
||||||
docker run --name mailcow-backup --rm \
|
docker run --name mailcow-backup --rm \
|
||||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:ro,z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:ro,z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_postfix.tar.gz /postfix
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_postfix.tar.gz /postfix
|
||||||
;;&
|
;;&
|
||||||
mysql|all)
|
mysql|all)
|
||||||
SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE})
|
SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE})
|
||||||
@@ -191,7 +200,7 @@ function restore() {
|
|||||||
docker run -it --name mailcow-backup --rm \
|
docker run -it --name mailcow-backup --rm \
|
||||||
-v ${RESTORE_LOCATION}:/backup:z \
|
-v ${RESTORE_LOCATION}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_vmail.tar.gz
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_vmail.tar.gz
|
||||||
docker start $(docker ps -aqf name=dovecot-mailcow)
|
docker start $(docker ps -aqf name=dovecot-mailcow)
|
||||||
echo
|
echo
|
||||||
echo "In most cases it is not required to run a full resync, you can run the command printed below at any time after testing wether the restore process broke a mailbox:"
|
echo "In most cases it is not required to run a full resync, you can run the command printed below at any time after testing wether the restore process broke a mailbox:"
|
||||||
@@ -210,7 +219,7 @@ function restore() {
|
|||||||
docker run -it --name mailcow-backup --rm \
|
docker run -it --name mailcow-backup --rm \
|
||||||
-v ${RESTORE_LOCATION}:/backup:z \
|
-v ${RESTORE_LOCATION}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_redis.tar.gz
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_redis.tar.gz
|
||||||
docker start $(docker ps -aqf name=redis-mailcow)
|
docker start $(docker ps -aqf name=redis-mailcow)
|
||||||
;;
|
;;
|
||||||
crypt)
|
crypt)
|
||||||
@@ -218,7 +227,7 @@ function restore() {
|
|||||||
docker run -it --name mailcow-backup --rm \
|
docker run -it --name mailcow-backup --rm \
|
||||||
-v ${RESTORE_LOCATION}:/backup:z \
|
-v ${RESTORE_LOCATION}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_crypt.tar.gz
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_crypt.tar.gz
|
||||||
docker start $(docker ps -aqf name=dovecot-mailcow)
|
docker start $(docker ps -aqf name=dovecot-mailcow)
|
||||||
;;
|
;;
|
||||||
rspamd)
|
rspamd)
|
||||||
@@ -226,7 +235,7 @@ function restore() {
|
|||||||
docker run -it --name mailcow-backup --rm \
|
docker run -it --name mailcow-backup --rm \
|
||||||
-v ${RESTORE_LOCATION}:/backup:z \
|
-v ${RESTORE_LOCATION}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_rspamd.tar.gz
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_rspamd.tar.gz
|
||||||
docker start $(docker ps -aqf name=rspamd-mailcow)
|
docker start $(docker ps -aqf name=rspamd-mailcow)
|
||||||
;;
|
;;
|
||||||
postfix)
|
postfix)
|
||||||
@@ -234,7 +243,7 @@ function restore() {
|
|||||||
docker run -it --name mailcow-backup --rm \
|
docker run -it --name mailcow-backup --rm \
|
||||||
-v ${RESTORE_LOCATION}:/backup:z \
|
-v ${RESTORE_LOCATION}:/backup:z \
|
||||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:z \
|
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:z \
|
||||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_postfix.tar.gz
|
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_postfix.tar.gz
|
||||||
docker start $(docker ps -aqf name=postfix-mailcow)
|
docker start $(docker ps -aqf name=postfix-mailcow)
|
||||||
;;
|
;;
|
||||||
mysql|mariadb)
|
mysql|mariadb)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
MASTER="en"
|
MASTER="en-gb"
|
||||||
|
|
||||||
DIR = "#{__dir__}/.."
|
DIR = "#{__dir__}/.."
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
##
|
##
|
||||||
## Set haproxy_trusted_networks in Dovecots extra.conf!
|
## Set haproxy_trusted_networks in Dovecots extra.conf!
|
||||||
#ä
|
##
|
||||||
|
|
||||||
version: '2.1'
|
version: '2.1'
|
||||||
services:
|
services:
|
||||||
|
@@ -50,7 +50,7 @@ echo -e "\e[32mTrying to determine GLIBC version...\e[0m"
|
|||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
echo -e "\e[33mWARNING: $COMPOSE_PATH is not writable, but new version $LATEST_COMPOSE is available (installed: $COMPOSE_VERSION)\e[0m"
|
echo -e "\e[33mWARNING: $COMPOSE_PATH is not writable, but new version $LATEST_COMPOSE is available (installed: $COMPOSE_VERSION)\e[0m"
|
||||||
return 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
@@ -3,9 +3,9 @@
|
|||||||
############## Begin Function Section ##############
|
############## Begin Function Section ##############
|
||||||
|
|
||||||
check_online_status() {
|
check_online_status() {
|
||||||
CHECK_ONLINE_IPS=(1.1.1.1 9.9.9.9 8.8.8.8)
|
CHECK_ONLINE_DOMAINS=('https://github.com' 'https://hub.docker.com')
|
||||||
for ip in "${CHECK_ONLINE_IPS[@]}"; do
|
for domain in "${CHECK_ONLINE_DOMAINS[@]}"; do
|
||||||
if timeout 3 ping -c 1 ${ip} > /dev/null; then
|
if timeout 6 curl --head --silent --output /dev/null ${domain}; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
Reference in New Issue
Block a user