Merge remote-tracking branch 'origin/staging' into add-podman-support
This commit is contained in:
commit
482854f7fa
|
@ -1,15 +1,19 @@
|
||||||
{
|
{
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"timezone": "Europe/Berlin",
|
"timezone": "Europe/Berlin",
|
||||||
"dependencyDashboard": false,
|
"dependencyDashboard": true,
|
||||||
"dependencyDashboardTitle": "Renovate Dashboard",
|
"dependencyDashboardTitle": "Renovate Dashboard",
|
||||||
"commitBody": "Signed-off-by: milkmaker <milkmaker@mailcow.de>",
|
"commitBody": "Signed-off-by: milkmaker <milkmaker@mailcow.de>",
|
||||||
"rebaseWhen": "auto",
|
"rebaseWhen": "auto",
|
||||||
|
"labels": ["renovate"],
|
||||||
"assignees": [
|
"assignees": [
|
||||||
"@magiccc"
|
"@magiccc"
|
||||||
],
|
],
|
||||||
"baseBranches": ["staging"],
|
"baseBranches": ["staging"],
|
||||||
"enabledManagers": ["github-actions", "regex"],
|
"enabledManagers": ["github-actions", "regex", "docker-compose"],
|
||||||
|
"ignorePaths": [
|
||||||
|
"data\/web\/inc\/lib\/vendor\/matthiasmullie\/minify\/**"
|
||||||
|
],
|
||||||
"regexManagers": [
|
"regexManagers": [
|
||||||
{
|
{
|
||||||
"fileMatch": ["^helper-scripts\/nextcloud.sh$"],
|
"fileMatch": ["^helper-scripts\/nextcloud.sh$"],
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
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'
|
|
|
@ -12,7 +12,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Run the Action
|
- name: Run the Action
|
||||||
uses: devops-infra/action-pull-request@v0.5.3
|
uses: devops-infra/action-pull-request@v0.5.5
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
||||||
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
||||||
|
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
|
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: data/Dockerfiles/backup/Dockerfile
|
file: data/Dockerfiles/backup/Dockerfile
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
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.2
|
|
||||||
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'
|
|
|
@ -1,6 +1,5 @@
|
||||||
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
||||||
|
|
||||||
[](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)
|
||||||
|
|
||||||
|
|
|
@ -213,11 +213,13 @@ while true; do
|
||||||
done
|
done
|
||||||
ADDITIONAL_WC_ARR+=('autodiscover' 'autoconfig')
|
ADDITIONAL_WC_ARR+=('autodiscover' 'autoconfig')
|
||||||
|
|
||||||
|
if [[ ${SKIP_IP_CHECK} != "y" ]]; then
|
||||||
# Start IP detection
|
# Start IP detection
|
||||||
log_f "Detecting IP addresses..."
|
log_f "Detecting IP addresses..."
|
||||||
IPV4=$(get_ipv4)
|
IPV4=$(get_ipv4)
|
||||||
IPV6=$(get_ipv6)
|
IPV6=$(get_ipv6)
|
||||||
log_f "OK: ${IPV4}, ${IPV6:-"0000:0000:0000:0000:0000:0000:0000:0000"}"
|
log_f "OK: ${IPV4}, ${IPV6:-"0000:0000:0000:0000:0000:0000:0000:0000"}"
|
||||||
|
fi
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
# IP and webroot challenge verification #
|
# IP and webroot challenge verification #
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM clamav/clamav:1.0_base
|
FROM clamav/clamav:1.0.1-1_base
|
||||||
|
|
||||||
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ RUN apk add --update --no-cache python3 \
|
||||||
fastapi \
|
fastapi \
|
||||||
uvicorn \
|
uvicorn \
|
||||||
aiodocker \
|
aiodocker \
|
||||||
|
docker \
|
||||||
redis
|
redis
|
||||||
|
|
||||||
COPY docker-entrypoint.sh /app/
|
COPY docker-entrypoint.sh /app/
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from fastapi import FastAPI, Response, Request
|
from fastapi import FastAPI, Response, Request
|
||||||
import aiodocker
|
import aiodocker
|
||||||
|
import docker
|
||||||
import psutil
|
import psutil
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
@ -9,11 +10,38 @@ import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import redis
|
import redis
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
from logging.config import dictConfig
|
||||||
|
|
||||||
|
|
||||||
|
log_config = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"default": {
|
||||||
|
"()": "uvicorn.logging.DefaultFormatter",
|
||||||
|
"fmt": "%(levelprefix)s %(asctime)s %(message)s",
|
||||||
|
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"default": {
|
||||||
|
"formatter": "default",
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"stream": "ext://sys.stderr",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"api-logger": {"handlers": ["default"], "level": "INFO"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dictConfig(log_config)
|
||||||
|
|
||||||
containerIds_to_update = []
|
containerIds_to_update = []
|
||||||
host_stats_isUpdating = False
|
host_stats_isUpdating = False
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
logger = logging.getLogger('api-logger')
|
||||||
|
|
||||||
|
|
||||||
@app.get("/host/stats")
|
@app.get("/host/stats")
|
||||||
|
@ -21,18 +49,15 @@ async def get_host_update_stats():
|
||||||
global host_stats_isUpdating
|
global host_stats_isUpdating
|
||||||
|
|
||||||
if host_stats_isUpdating == False:
|
if host_stats_isUpdating == False:
|
||||||
print("start host stats task")
|
|
||||||
asyncio.create_task(get_host_stats())
|
asyncio.create_task(get_host_stats())
|
||||||
host_stats_isUpdating = True
|
host_stats_isUpdating = True
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if redis_client.exists('host_stats'):
|
if redis_client.exists('host_stats'):
|
||||||
break
|
break
|
||||||
print("wait for host_stats results")
|
|
||||||
await asyncio.sleep(1.5)
|
await asyncio.sleep(1.5)
|
||||||
|
|
||||||
|
|
||||||
print("host stats pulled")
|
|
||||||
stats = json.loads(redis_client.get('host_stats'))
|
stats = json.loads(redis_client.get('host_stats'))
|
||||||
return Response(content=json.dumps(stats, indent=4), media_type="application/json")
|
return Response(content=json.dumps(stats, indent=4), media_type="application/json")
|
||||||
|
|
||||||
|
@ -106,14 +131,14 @@ async def post_containers(container_id : str, post_action : str, request: Reques
|
||||||
else:
|
else:
|
||||||
api_call_method_name = '__'.join(['container_post', str(post_action) ])
|
api_call_method_name = '__'.join(['container_post', str(post_action) ])
|
||||||
|
|
||||||
docker_utils = DockerUtils(async_docker_client)
|
docker_utils = DockerUtils(sync_docker_client)
|
||||||
api_call_method = getattr(docker_utils, api_call_method_name, lambda container_id: Response(content=json.dumps({'type': 'danger', 'msg':'container_post - unknown api call' }, indent=4), media_type="application/json"))
|
api_call_method = getattr(docker_utils, api_call_method_name, lambda container_id: Response(content=json.dumps({'type': 'danger', 'msg':'container_post - unknown api call' }, indent=4), media_type="application/json"))
|
||||||
|
|
||||||
|
|
||||||
print("api call: %s, container_id: %s" % (api_call_method_name, container_id))
|
logger.info("api call: %s, container_id: %s" % (api_call_method_name, container_id))
|
||||||
return await api_call_method(container_id, request_json)
|
return api_call_method(container_id, request_json)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("error - container_post: %s" % str(e))
|
logger.error("error - container_post: %s" % str(e))
|
||||||
res = {
|
res = {
|
||||||
"type": "danger",
|
"type": "danger",
|
||||||
"msg": str(e)
|
"msg": str(e)
|
||||||
|
@ -152,398 +177,289 @@ class DockerUtils:
|
||||||
self.docker_client = docker_client
|
self.docker_client = docker_client
|
||||||
|
|
||||||
# api call: container_post - post_action: stop
|
# api call: container_post - post_action: stop
|
||||||
async def container_post__stop(self, container_id, request_json):
|
def container_post__stop(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
container.stop()
|
||||||
await container.stop()
|
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'command completed successfully'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
|
res = { 'type': 'success', 'msg': 'command completed successfully'}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
# api call: container_post - post_action: start
|
# api call: container_post - post_action: start
|
||||||
async def container_post__start(self, container_id, request_json):
|
def container_post__start(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
container.start()
|
||||||
await container.start()
|
|
||||||
res = {
|
res = { 'type': 'success', 'msg': 'command completed successfully'}
|
||||||
'type': 'success',
|
|
||||||
'msg': 'command completed successfully'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: restart
|
# api call: container_post - post_action: restart
|
||||||
async def container_post__restart(self, container_id, request_json):
|
def container_post__restart(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
container.restart()
|
||||||
await container.restart()
|
|
||||||
res = {
|
res = { 'type': 'success', 'msg': 'command completed successfully'}
|
||||||
'type': 'success',
|
|
||||||
'msg': 'command completed successfully'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: top
|
# api call: container_post - post_action: top
|
||||||
async def container_post__top(self, container_id, request_json):
|
def container_post__top(self, container_id, request_json):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
res = { 'type': 'success', 'msg': container.top()}
|
||||||
ps_exec = await container.exec("ps")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
async with ps_exec.start(detach=False) as stream:
|
# api call: container_post - post_action: stats
|
||||||
ps_return = await stream.read_out()
|
def container_post__stats(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(all=True, filters={"id": container_id}):
|
||||||
exec_details = await ps_exec.inspect()
|
for stat in container.stats(decode=True, stream=True):
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
res = { 'type': 'success', 'msg': stat}
|
||||||
res = {
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
'type': 'success',
|
|
||||||
'msg': ps_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'danger',
|
|
||||||
'msg': ''
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: delete
|
# api call: container_post - post_action: exec - cmd: mailq - task: delete
|
||||||
async def container_post__exec__mailq__delete(self, container_id, request_json):
|
def container_post__exec__mailq__delete(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
flagged_qids = ['-d %s' % i for i in filtered_qids]
|
flagged_qids = ['-d %s' % i for i in filtered_qids]
|
||||||
sanitized_string = str(' '.join(flagged_qids))
|
sanitized_string = str(' '.join(flagged_qids));
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
||||||
|
return exec_run_handler('generic', postsuper_r)
|
||||||
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: hold
|
# api call: container_post - post_action: exec - cmd: mailq - task: hold
|
||||||
async def container_post__exec__mailq__hold(self, container_id, request_json):
|
def container_post__exec__mailq__hold(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
flagged_qids = ['-h %s' % i for i in filtered_qids]
|
flagged_qids = ['-h %s' % i for i in filtered_qids]
|
||||||
sanitized_string = str(' '.join(flagged_qids))
|
sanitized_string = str(' '.join(flagged_qids));
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
for container in (await self.docker_client.containers.list()):
|
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
||||||
if container._id == container_id:
|
return exec_run_handler('generic', postsuper_r)
|
||||||
postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: cat
|
# api call: container_post - post_action: exec - cmd: mailq - task: cat
|
||||||
async def container_post__exec__mailq__cat(self, container_id, request_json):
|
def container_post__exec__mailq__cat(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
sanitized_string = str(' '.join(filtered_qids))
|
sanitized_string = str(' '.join(filtered_qids));
|
||||||
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
if container._id == container_id:
|
postcat_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postcat -q " + sanitized_string], user='postfix')
|
||||||
postcat_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postcat -q " + sanitized_string], user='postfix')
|
if not postcat_return:
|
||||||
return await exec_run_handler('utf8_text_only', postcat_exec)
|
postcat_return = 'err: invalid'
|
||||||
|
return exec_run_handler('utf8_text_only', postcat_return)
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: unhold
|
# api call: container_post - post_action: exec - cmd: mailq - task: unhold
|
||||||
async def container_post__exec__mailq__unhold(self, container_id, request_json):
|
def container_post__exec__mailq__unhold(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
flagged_qids = ['-H %s' % i for i in filtered_qids]
|
flagged_qids = ['-H %s' % i for i in filtered_qids]
|
||||||
sanitized_string = str(' '.join(flagged_qids))
|
sanitized_string = str(' '.join(flagged_qids));
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
for container in (await self.docker_client.containers.list()):
|
postsuper_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
||||||
if container._id == container_id:
|
return exec_run_handler('generic', postsuper_r)
|
||||||
postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postsuper " + sanitized_string])
|
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: deliver
|
# api call: container_post - post_action: exec - cmd: mailq - task: deliver
|
||||||
async def container_post__exec__mailq__deliver(self, container_id, request_json):
|
def container_post__exec__mailq__deliver(self, container_id, request_json):
|
||||||
if 'items' in request_json:
|
if 'items' in request_json:
|
||||||
r = re.compile("^[0-9a-fA-F]+$")
|
r = re.compile("^[0-9a-fA-F]+$")
|
||||||
filtered_qids = filter(r.match, request_json['items'])
|
filtered_qids = filter(r.match, request_json['items'])
|
||||||
if filtered_qids:
|
if filtered_qids:
|
||||||
flagged_qids = ['-i %s' % i for i in filtered_qids]
|
flagged_qids = ['-i %s' % i for i in filtered_qids]
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
for container in (await self.docker_client.containers.list()):
|
for i in flagged_qids:
|
||||||
if container._id == container_id:
|
postqueue_r = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix')
|
||||||
for i in flagged_qids:
|
# todo: check each exit code
|
||||||
postsuper_r_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postqueue " + i], user='postfix')
|
res = { 'type': 'success', 'msg': 'Scheduled immediate delivery'}
|
||||||
async with postsuper_r_exec.start(detach=False) as stream:
|
|
||||||
postsuper_r_return = await stream.read_out()
|
|
||||||
# todo: check each exit code
|
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'Scheduled immediate delivery'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: list
|
|
||||||
async def container_post__exec__mailq__list(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
mailq_exec = await container.exec(["/usr/sbin/postqueue", "-j"], user='postfix')
|
|
||||||
return await exec_run_handler('utf8_text_only', mailq_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: flush
|
|
||||||
async def container_post__exec__mailq__flush(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
postsuper_r_exec = await container.exec(["/usr/sbin/postqueue", "-f"], user='postfix')
|
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: mailq - task: super_delete
|
|
||||||
async def container_post__exec__mailq__super_delete(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
postsuper_r_exec = await container.exec(["/usr/sbin/postsuper", "-d", "ALL"])
|
|
||||||
return await exec_run_handler('generic', postsuper_r_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: system - task: fts_rescan
|
|
||||||
async def container_post__exec__system__fts_rescan(self, container_id, request_json):
|
|
||||||
if 'username' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
rescan_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -u '" + request_json['username'].replace("'", "'\\''") + "'"], user='vmail')
|
|
||||||
async with rescan_exec.start(detach=False) as stream:
|
|
||||||
rescan_return = await stream.read_out()
|
|
||||||
|
|
||||||
exec_details = await rescan_exec.inspect()
|
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'fts_rescan: rescan triggered'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'warning',
|
|
||||||
'msg': 'fts_rescan error'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
if 'all' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
rescan_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -A"], user='vmail')
|
|
||||||
async with rescan_exec.start(detach=False) as stream:
|
|
||||||
rescan_return = await stream.read_out()
|
|
||||||
|
|
||||||
exec_details = await rescan_exec.inspect()
|
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'fts_rescan: rescan triggered'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'warning',
|
|
||||||
'msg': 'fts_rescan error'
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: system - task: df
|
|
||||||
async def container_post__exec__system__df(self, container_id, request_json):
|
|
||||||
if 'dir' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
df_exec = await container.exec(["/bin/bash", "-c", "/bin/df -H '" + request_json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody')
|
|
||||||
async with df_exec.start(detach=False) as stream:
|
|
||||||
df_return = await stream.read_out()
|
|
||||||
|
|
||||||
print(df_return)
|
|
||||||
print(await df_exec.inspect())
|
|
||||||
exec_details = await df_exec.inspect()
|
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
|
||||||
return df_return.data.decode('utf-8').rstrip()
|
|
||||||
else:
|
|
||||||
return "0,0,0,0,0,0"
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
|
|
||||||
async def container_post__exec__system__mysql_upgrade(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
sql_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/mysql_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n"], user='mysql')
|
|
||||||
async with sql_exec.start(detach=False) as stream:
|
|
||||||
sql_return = await stream.read_out()
|
|
||||||
|
|
||||||
exec_details = await sql_exec.inspect()
|
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
|
||||||
matched = False
|
|
||||||
for line in sql_return.data.decode('utf-8').split("\n"):
|
|
||||||
if 'is already upgraded to' in line:
|
|
||||||
matched = True
|
|
||||||
if matched:
|
|
||||||
res = {
|
|
||||||
'type': 'success',
|
|
||||||
'msg': 'mysql_upgrade: already upgraded',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
await container.restart()
|
|
||||||
res = {
|
|
||||||
'type': 'warning',
|
|
||||||
'msg': 'mysql_upgrade: upgrade was applied',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'error',
|
|
||||||
'msg': 'mysql_upgrade: error running command',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: system - task: mysql_tzinfo_to_sql
|
|
||||||
async def container_post__exec__system__mysql_tzinfo_to_sql(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
sql_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/mysql_tzinfo_to_sql /usr/share/zoneinfo | /bin/sed 's/Local time zone must be set--see zic manual page/FCTY/' | /usr/bin/mysql -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "' mysql \n"], user='mysql')
|
|
||||||
async with sql_exec.start(detach=False) as stream:
|
|
||||||
sql_return = await stream.read_out()
|
|
||||||
|
|
||||||
exec_details = await sql_exec.inspect()
|
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
|
||||||
res = {
|
|
||||||
'type': 'info',
|
|
||||||
'msg': 'mysql_tzinfo_to_sql: command completed successfully',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
else:
|
|
||||||
res = {
|
|
||||||
'type': 'error',
|
|
||||||
'msg': 'mysql_tzinfo_to_sql: error running command',
|
|
||||||
'text': sql_return.data.decode('utf-8')
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: reload - task: dovecot
|
|
||||||
async def container_post__exec__reload__dovecot(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
reload_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/dovecot reload"])
|
|
||||||
return await exec_run_handler('generic', reload_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: reload - task: postfix
|
|
||||||
async def container_post__exec__reload__postfix(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
reload_exec = await container.exec(["/bin/bash", "-c", "/usr/sbin/postfix reload"])
|
|
||||||
return await exec_run_handler('generic', reload_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: reload - task: nginx
|
|
||||||
async def container_post__exec__reload__nginx(self, container_id, request_json):
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
reload_exec = await container.exec(["/bin/sh", "-c", "/usr/sbin/nginx -s reload"])
|
|
||||||
return await exec_run_handler('generic', reload_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: sieve - task: list
|
|
||||||
async def container_post__exec__sieve__list(self, container_id, request_json):
|
|
||||||
if 'username' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
sieve_exec = await container.exec(["/bin/bash", "-c", "/usr/bin/doveadm sieve list -u '" + request_json['username'].replace("'", "'\\''") + "'"])
|
|
||||||
return await exec_run_handler('utf8_text_only', sieve_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: sieve - task: print
|
|
||||||
async def container_post__exec__sieve__print(self, container_id, request_json):
|
|
||||||
if 'username' in request_json and 'script_name' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]
|
|
||||||
sieve_exec = await container.exec(cmd)
|
|
||||||
return await exec_run_handler('utf8_text_only', sieve_exec)
|
|
||||||
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: maildir - task: cleanup
|
|
||||||
async def container_post__exec__maildir__cleanup(self, container_id, request_json):
|
|
||||||
if 'maildir' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
sane_name = re.sub(r'\W+', '', request_json['maildir'])
|
|
||||||
cmd = ["/bin/bash", "-c", "if [[ -d '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' ]]; then /bin/mv '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"]
|
|
||||||
maildir_cleanup_exec = await container.exec(cmd, user='vmail')
|
|
||||||
return await exec_run_handler('generic', maildir_cleanup_exec)
|
|
||||||
|
|
||||||
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
|
|
||||||
async def container_post__exec__rspamd__worker_password(self, container_id, request_json):
|
|
||||||
if 'raw' in request_json:
|
|
||||||
for container in (await self.docker_client.containers.list()):
|
|
||||||
if container._id == container_id:
|
|
||||||
|
|
||||||
cmd = "./set_worker_password.sh '" + request_json['raw'].replace("'", "'\\''") + "' 2> /dev/null"
|
# api call: container_post - post_action: exec - cmd: mailq - task: list
|
||||||
rspamd_password_exec = await container.exec(cmd, user='_rspamd')
|
def container_post__exec__mailq__list(self, container_id, request_json):
|
||||||
async with rspamd_password_exec.start(detach=False) as stream:
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
rspamd_password_return = await stream.read_out()
|
mailq_return = container.exec_run(["/usr/sbin/postqueue", "-j"], user='postfix')
|
||||||
|
return exec_run_handler('utf8_text_only', mailq_return)
|
||||||
matched = False
|
# api call: container_post - post_action: exec - cmd: mailq - task: flush
|
||||||
if "OK" in rspamd_password_return.data.decode('utf-8'):
|
def container_post__exec__mailq__flush(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
postqueue_r = container.exec_run(["/usr/sbin/postqueue", "-f"], user='postfix')
|
||||||
|
return exec_run_handler('generic', postqueue_r)
|
||||||
|
# api call: container_post - post_action: exec - cmd: mailq - task: super_delete
|
||||||
|
def container_post__exec__mailq__super_delete(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
postsuper_r = container.exec_run(["/usr/sbin/postsuper", "-d", "ALL"])
|
||||||
|
return exec_run_handler('generic', postsuper_r)
|
||||||
|
# api call: container_post - post_action: exec - cmd: system - task: fts_rescan
|
||||||
|
def container_post__exec__system__fts_rescan(self, container_id, request_json):
|
||||||
|
if 'username' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -u '" + request_json['username'].replace("'", "'\\''") + "'"], user='vmail')
|
||||||
|
if rescan_return.exit_code == 0:
|
||||||
|
res = { 'type': 'success', 'msg': 'fts_rescan: rescan triggered'}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
res = { 'type': 'warning', 'msg': 'fts_rescan error'}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
if 'all' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
rescan_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm fts rescan -A"], user='vmail')
|
||||||
|
if rescan_return.exit_code == 0:
|
||||||
|
res = { 'type': 'success', 'msg': 'fts_rescan: rescan triggered'}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
res = { 'type': 'warning', 'msg': 'fts_rescan error'}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
# api call: container_post - post_action: exec - cmd: system - task: df
|
||||||
|
def container_post__exec__system__df(self, container_id, request_json):
|
||||||
|
if 'dir' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
df_return = container.exec_run(["/bin/bash", "-c", "/bin/df -H '" + request_json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody')
|
||||||
|
if df_return.exit_code == 0:
|
||||||
|
return df_return.output.decode('utf-8').rstrip()
|
||||||
|
else:
|
||||||
|
return "0,0,0,0,0,0"
|
||||||
|
# api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
|
||||||
|
def container_post__exec__system__mysql_upgrade(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
sql_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/mysql_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n"], user='mysql')
|
||||||
|
if sql_return.exit_code == 0:
|
||||||
|
matched = False
|
||||||
|
for line in sql_return.output.decode('utf-8').split("\n"):
|
||||||
|
if 'is already upgraded to' in line:
|
||||||
matched = True
|
matched = True
|
||||||
await container.restart()
|
if matched:
|
||||||
|
res = { 'type': 'success', 'msg':'mysql_upgrade: already upgraded', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
container.restart()
|
||||||
|
res = { 'type': 'warning', 'msg':'mysql_upgrade: upgrade was applied', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
res = { 'type': 'error', 'msg': 'mysql_upgrade: error running command', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
# api call: container_post - post_action: exec - cmd: system - task: mysql_tzinfo_to_sql
|
||||||
|
def container_post__exec__system__mysql_tzinfo_to_sql(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
sql_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/mysql_tzinfo_to_sql /usr/share/zoneinfo | /bin/sed 's/Local time zone must be set--see zic manual page/FCTY/' | /usr/bin/mysql -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "' mysql \n"], user='mysql')
|
||||||
|
if sql_return.exit_code == 0:
|
||||||
|
res = { 'type': 'info', 'msg': 'mysql_tzinfo_to_sql: command completed successfully', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
res = { 'type': 'error', 'msg': 'mysql_tzinfo_to_sql: error running command', 'text': sql_return.output.decode('utf-8')}
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
# api call: container_post - post_action: exec - cmd: reload - task: dovecot
|
||||||
|
def container_post__exec__reload__dovecot(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
reload_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/dovecot reload"])
|
||||||
|
return exec_run_handler('generic', reload_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: reload - task: postfix
|
||||||
|
def container_post__exec__reload__postfix(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
reload_return = container.exec_run(["/bin/bash", "-c", "/usr/sbin/postfix reload"])
|
||||||
|
return exec_run_handler('generic', reload_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: reload - task: nginx
|
||||||
|
def container_post__exec__reload__nginx(self, container_id, request_json):
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
reload_return = container.exec_run(["/bin/sh", "-c", "/usr/sbin/nginx -s reload"])
|
||||||
|
return exec_run_handler('generic', reload_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: sieve - task: list
|
||||||
|
def container_post__exec__sieve__list(self, container_id, request_json):
|
||||||
|
if 'username' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/doveadm sieve list -u '" + request_json['username'].replace("'", "'\\''") + "'"])
|
||||||
|
return exec_run_handler('utf8_text_only', sieve_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: sieve - task: print
|
||||||
|
def container_post__exec__sieve__print(self, container_id, request_json):
|
||||||
|
if 'username' in request.json and 'script_name' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]
|
||||||
|
sieve_return = container.exec_run(cmd)
|
||||||
|
return exec_run_handler('utf8_text_only', sieve_return)
|
||||||
|
# api call: container_post - post_action: exec - cmd: maildir - task: cleanup
|
||||||
|
def container_post__exec__maildir__cleanup(self, container_id, request_json):
|
||||||
|
if 'maildir' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
sane_name = re.sub(r'\W+', '', request_json['maildir'])
|
||||||
|
cmd = ["/bin/bash", "-c", "if [[ -d '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' ]]; then /bin/mv '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"]
|
||||||
|
maildir_cleanup = container.exec_run(cmd, user='vmail')
|
||||||
|
return exec_run_handler('generic', maildir_cleanup)
|
||||||
|
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
|
||||||
|
def container_post__exec__rspamd__worker_password(self, container_id, request_json):
|
||||||
|
if 'raw' in request_json:
|
||||||
|
for container in self.docker_client.containers.list(filters={"id": container_id}):
|
||||||
|
cmd = "/usr/bin/rspamadm pw -e -p '" + request_json['raw'].replace("'", "'\\''") + "' 2> /dev/null"
|
||||||
|
cmd_response = exec_cmd_container(container, cmd, user="_rspamd")
|
||||||
|
|
||||||
if matched:
|
matched = False
|
||||||
res = {
|
for line in cmd_response.split("\n"):
|
||||||
'type': 'success',
|
if '$2$' in line:
|
||||||
'msg': 'command completed successfully'
|
hash = line.strip()
|
||||||
}
|
hash_out = re.search('\$2\$.+$', hash).group(0)
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
rspamd_passphrase_hash = re.sub('[^0-9a-zA-Z\$]+', '', hash_out.rstrip())
|
||||||
else:
|
rspamd_password_filename = "/etc/rspamd/override.d/worker-controller-password.inc"
|
||||||
res = {
|
cmd = '''/bin/echo 'enable_password = "%s";' > %s && cat %s''' % (rspamd_passphrase_hash, rspamd_password_filename, rspamd_password_filename)
|
||||||
'type': 'danger',
|
cmd_response = exec_cmd_container(container, cmd, user="_rspamd")
|
||||||
'msg': 'command did not complete'
|
if rspamd_passphrase_hash.startswith("$2$") and rspamd_passphrase_hash in cmd_response:
|
||||||
}
|
container.restart()
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
matched = True
|
||||||
|
if matched:
|
||||||
|
res = { 'type': 'success', 'msg': 'command completed successfully' }
|
||||||
|
logger.info('success changing Rspamd password')
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
else:
|
||||||
|
logger.error('failed changing Rspamd password')
|
||||||
|
res = { 'type': 'danger', 'msg': 'command did not complete' }
|
||||||
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
|
def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"):
|
||||||
|
|
||||||
async def exec_run_handler(type, exec_obj):
|
def recv_socket_data(c_socket, timeout):
|
||||||
async with exec_obj.start(detach=False) as stream:
|
c_socket.setblocking(0)
|
||||||
exec_return = await stream.read_out()
|
total_data=[]
|
||||||
|
data=''
|
||||||
|
begin=time.time()
|
||||||
|
while True:
|
||||||
|
if total_data and time.time()-begin > timeout:
|
||||||
|
break
|
||||||
|
elif time.time()-begin > timeout*2:
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
data = c_socket.recv(8192)
|
||||||
|
if data:
|
||||||
|
total_data.append(data.decode('utf-8'))
|
||||||
|
#change the beginning time for measurement
|
||||||
|
begin=time.time()
|
||||||
|
else:
|
||||||
|
#sleep for sometime to indicate a gap
|
||||||
|
time.sleep(0.1)
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return ''.join(total_data)
|
||||||
|
|
||||||
|
|
||||||
if exec_return == None:
|
try :
|
||||||
exec_return = ""
|
socket = container.exec_run([shell_cmd], stdin=True, socket=True, user=user).output._sock
|
||||||
else:
|
if not cmd.endswith("\n"):
|
||||||
exec_return = exec_return.data.decode('utf-8')
|
cmd = cmd + "\n"
|
||||||
|
socket.send(cmd.encode('utf-8'))
|
||||||
if type == 'generic':
|
data = recv_socket_data(socket, timeout)
|
||||||
exec_details = await exec_obj.inspect()
|
socket.close()
|
||||||
if exec_details["ExitCode"] == None or exec_details["ExitCode"] == 0:
|
return data
|
||||||
res = {
|
except Exception as e:
|
||||||
"type": "success",
|
logger.error("error - exec_cmd_container: %s" % str(e))
|
||||||
"msg": "command completed successfully"
|
traceback.print_exc(file=sys.stdout)
|
||||||
}
|
def exec_run_handler(type, output):
|
||||||
|
if type == 'generic':
|
||||||
|
if output.exit_code == 0:
|
||||||
|
res = { 'type': 'success', 'msg': 'command completed successfully' }
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
else:
|
else:
|
||||||
res = {
|
res = { 'type': 'danger', 'msg': 'command failed: ' + output.output.decode('utf-8') }
|
||||||
"type": "success",
|
|
||||||
"msg": "'command failed: " + exec_return
|
|
||||||
}
|
|
||||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||||
if type == 'utf8_text_only':
|
if type == 'utf8_text_only':
|
||||||
return Response(content=exec_return, media_type="text/plain")
|
return Response(content=output.output.decode('utf-8'), media_type="text/plain")
|
||||||
|
|
||||||
async def get_host_stats(wait=5):
|
async def get_host_stats(wait=5):
|
||||||
global host_stats_isUpdating
|
global host_stats_isUpdating
|
||||||
|
@ -570,12 +486,10 @@ async def get_host_stats(wait=5):
|
||||||
"type": "danger",
|
"type": "danger",
|
||||||
"msg": str(e)
|
"msg": str(e)
|
||||||
}
|
}
|
||||||
print(json.dumps(res, indent=4))
|
|
||||||
|
|
||||||
await asyncio.sleep(wait)
|
await asyncio.sleep(wait)
|
||||||
host_stats_isUpdating = False
|
host_stats_isUpdating = False
|
||||||
|
|
||||||
|
|
||||||
async def get_container_stats(container_id, wait=5, stop=False):
|
async def get_container_stats(container_id, wait=5, stop=False):
|
||||||
global containerIds_to_update
|
global containerIds_to_update
|
||||||
|
|
||||||
|
@ -598,13 +512,11 @@ async def get_container_stats(container_id, wait=5, stop=False):
|
||||||
"type": "danger",
|
"type": "danger",
|
||||||
"msg": str(e)
|
"msg": str(e)
|
||||||
}
|
}
|
||||||
print(json.dumps(res, indent=4))
|
|
||||||
else:
|
else:
|
||||||
res = {
|
res = {
|
||||||
"type": "danger",
|
"type": "danger",
|
||||||
"msg": "no or invalid id defined"
|
"msg": "no or invalid id defined"
|
||||||
}
|
}
|
||||||
print(json.dumps(res, indent=4))
|
|
||||||
|
|
||||||
await asyncio.sleep(wait)
|
await asyncio.sleep(wait)
|
||||||
if stop == True:
|
if stop == True:
|
||||||
|
@ -615,9 +527,13 @@ async def get_container_stats(container_id, wait=5, stop=False):
|
||||||
await get_container_stats(container_id, wait=0, stop=True)
|
await get_container_stats(container_id, wait=0, stop=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if os.environ['REDIS_SLAVEOF_IP'] != "":
|
if os.environ['REDIS_SLAVEOF_IP'] != "":
|
||||||
redis_client = redis.Redis(host=os.environ['REDIS_SLAVEOF_IP'], port=os.environ['REDIS_SLAVEOF_PORT'], db=0)
|
redis_client = redis.Redis(host=os.environ['REDIS_SLAVEOF_IP'], port=os.environ['REDIS_SLAVEOF_PORT'], db=0)
|
||||||
else:
|
else:
|
||||||
redis_client = redis.Redis(host='redis-mailcow', port=6379, db=0)
|
redis_client = redis.Redis(host='redis-mailcow', port=6379, db=0)
|
||||||
|
|
||||||
|
sync_docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
|
||||||
async_docker_client = aiodocker.Docker(url='unix:///var/run/docker.sock')
|
async_docker_client = aiodocker.Docker(url='unix:///var/run/docker.sock')
|
||||||
|
|
||||||
|
logger.info('DockerApi started')
|
||||||
|
|
|
@ -2,7 +2,8 @@ FROM debian:bullseye-slim
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ARG DOVECOT=2.3.19.1
|
# renovate: datasource=github-tags depName=dovecot/core versioning=semver-coerced
|
||||||
|
ARG DOVECOT=2.3.20
|
||||||
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced
|
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced
|
||||||
ARG GOSU_VERSION=1.16
|
ARG GOSU_VERSION=1.16
|
||||||
ENV LC_ALL C
|
ENV LC_ALL C
|
||||||
|
|
|
@ -359,21 +359,28 @@ def snat4(snat_target):
|
||||||
chain = iptc.Chain(table, 'POSTROUTING')
|
chain = iptc.Chain(table, 'POSTROUTING')
|
||||||
table.autocommit = False
|
table.autocommit = False
|
||||||
new_rule = get_snat4_rule()
|
new_rule = get_snat4_rule()
|
||||||
for position, rule in enumerate(chain.rules):
|
|
||||||
match = all((
|
if not chain.rules:
|
||||||
new_rule.get_src() == rule.get_src(),
|
# if there are no rules in the chain, insert the new rule directly
|
||||||
new_rule.get_dst() == rule.get_dst(),
|
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
|
||||||
new_rule.target.parameters == rule.target.parameters,
|
chain.insert_rule(new_rule)
|
||||||
new_rule.target.name == rule.target.name
|
else:
|
||||||
))
|
for position, rule in enumerate(chain.rules):
|
||||||
if position == 0:
|
match = all((
|
||||||
if not match:
|
new_rule.get_src() == rule.get_src(),
|
||||||
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
|
new_rule.get_dst() == rule.get_dst(),
|
||||||
chain.insert_rule(new_rule)
|
new_rule.target.parameters == rule.target.parameters,
|
||||||
else:
|
new_rule.target.name == rule.target.name
|
||||||
if match:
|
))
|
||||||
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)
|
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.commit()
|
||||||
table.autocommit = True
|
table.autocommit = True
|
||||||
except:
|
except:
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
FROM php:8.1-fpm-alpine3.17
|
FROM php:8.1-fpm-alpine3.17
|
||||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||||
|
|
||||||
ENV APCU_PECL 5.1.22
|
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced
|
||||||
ENV IMAGICK_PECL 3.7.0
|
ARG APCU_PECL_VERSION=5.1.22
|
||||||
ENV MAILPARSE_PECL 3.1.4
|
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced
|
||||||
ENV MEMCACHED_PECL 3.2.0
|
ARG IMAGICK_PECL_VERSION=3.7.0
|
||||||
ENV REDIS_PECL 5.3.7
|
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced
|
||||||
ENV COMPOSER 2.4.4
|
ARG MAILPARSE_PECL_VERSION=3.1.4
|
||||||
|
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced
|
||||||
|
ARG MEMCACHED_PECL_VERSION=3.2.0
|
||||||
|
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced
|
||||||
|
ARG REDIS_PECL_VERSION=5.3.7
|
||||||
|
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced
|
||||||
|
ARG COMPOSER_VERSION=2.5.4
|
||||||
|
|
||||||
RUN apk add -U --no-cache autoconf \
|
RUN apk add -U --no-cache autoconf \
|
||||||
aspell-dev \
|
aspell-dev \
|
||||||
|
@ -55,11 +61,11 @@ RUN apk add -U --no-cache autoconf \
|
||||||
samba-client \
|
samba-client \
|
||||||
zlib-dev \
|
zlib-dev \
|
||||||
tzdata \
|
tzdata \
|
||||||
&& pecl install mailparse-${MAILPARSE_PECL} \
|
&& pecl install APCu-${APCU_PECL_VERSION} \
|
||||||
&& pecl install redis-${REDIS_PECL} \
|
&& pecl install imagick-${IMAGICK_PECL_VERSION} \
|
||||||
&& pecl install memcached-${MEMCACHED_PECL} \
|
&& pecl install mailparse-${MAILPARSE_PECL_VERSION} \
|
||||||
&& pecl install APCu-${APCU_PECL} \
|
&& pecl install memcached-${MEMCACHED_PECL_VERSION} \
|
||||||
&& pecl install imagick-${IMAGICK_PECL} \
|
&& pecl install redis-${REDIS_PECL_VERSION} \
|
||||||
&& 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 \
|
||||||
|
@ -72,7 +78,7 @@ RUN apk add -U --no-cache autoconf \
|
||||||
&& 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 -- --version=${COMPOSER} \
|
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \
|
||||||
&& 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 \
|
||||||
|
@ -102,4 +108,4 @@ COPY ./docker-entrypoint.sh /
|
||||||
|
|
||||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
|
|
||||||
CMD ["php-fpm"]
|
CMD ["php-fpm"]
|
|
@ -175,7 +175,7 @@ BAD_SUBJECT_00 {
|
||||||
type = "header";
|
type = "header";
|
||||||
header = "subject";
|
header = "subject";
|
||||||
regexp = true;
|
regexp = true;
|
||||||
map = "http://nullnull.org/bad-subject-regex.txt";
|
map = "http://fuzzy.mailcow.email/bad-subject-regex.txt";
|
||||||
score = 6.0;
|
score = 6.0;
|
||||||
symbols_set = ["BAD_SUBJECT_00"];
|
symbols_set = ["BAD_SUBJECT_00"];
|
||||||
}
|
}
|
||||||
|
|
|
@ -699,6 +699,38 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
summary: Create Domain Admin user
|
summary: Create Domain Admin user
|
||||||
|
/api/v1/add/sso/domain-admin:
|
||||||
|
post:
|
||||||
|
responses:
|
||||||
|
"401":
|
||||||
|
$ref: "#/components/responses/Unauthorized"
|
||||||
|
"200":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
examples:
|
||||||
|
response:
|
||||||
|
value:
|
||||||
|
token: "591F6D-5C3DD2-7455CD-DAF1C1-AA4FCC"
|
||||||
|
description: OK
|
||||||
|
headers: { }
|
||||||
|
tags:
|
||||||
|
- Single Sign-On
|
||||||
|
description: >-
|
||||||
|
Using this endpoint you can issue a token for Domain Admin user. This token can be used for
|
||||||
|
autologin Domain Admin user by using query_string var sso_token={token}. Token expiration time is 30s
|
||||||
|
operationId: Issue Domain Admin SSO token
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
example:
|
||||||
|
username: testadmin
|
||||||
|
properties:
|
||||||
|
username:
|
||||||
|
description: the username for the admin user
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
summary: Issue Domain Admin SSO token
|
||||||
/api/v1/edit/da-acl:
|
/api/v1/edit/da-acl:
|
||||||
post:
|
post:
|
||||||
responses:
|
responses:
|
||||||
|
@ -1999,7 +2031,7 @@ paths:
|
||||||
- domain.tld
|
- domain.tld
|
||||||
- domain2.tld
|
- domain2.tld
|
||||||
properties:
|
properties:
|
||||||
items:
|
items:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
|
@ -2993,7 +3025,7 @@ paths:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
log:
|
log:
|
||||||
|
@ -5586,6 +5618,8 @@ tags:
|
||||||
description: Manage DKIM keys
|
description: Manage DKIM keys
|
||||||
- name: Domain admin
|
- name: Domain admin
|
||||||
description: Create or udpdate domain admin users
|
description: Create or udpdate domain admin users
|
||||||
|
- name: Single Sign-On
|
||||||
|
description: Issue tokens for users
|
||||||
- name: Address Rewriting
|
- name: Address Rewriting
|
||||||
description: Create BCC maps or recipient maps
|
description: Create BCC maps or recipient maps
|
||||||
- name: Outgoing TLS Policy Map Overrides
|
- name: Outgoing TLS Policy Map Overrides
|
||||||
|
|
|
@ -77,4 +77,22 @@ li .dtr-data {
|
||||||
table.dataTable>tbody>tr.child span.dtr-title {
|
table.dataTable>tbody>tr.child span.dtr-title {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.dataTables_wrapper div.dataTables_filter {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
div.dataTables_wrapper div.dataTables_length {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.dataTables_paginate, .dataTables_length, .dataTables_filter {
|
||||||
|
margin: 10px 0!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.dt-text-right {
|
||||||
|
text-align: end !important;
|
||||||
|
}
|
||||||
|
th.dt-text-right {
|
||||||
|
text-align: end !important;
|
||||||
|
}
|
||||||
|
|
|
@ -370,14 +370,3 @@ button[aria-expanded='true'] > .caret {
|
||||||
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
|
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
|
||||||
background-color: #f0f0f0 !important;
|
background-color: #f0f0f0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
div.dataTables_wrapper div.dataTables_filter {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
div.dataTables_wrapper div.dataTables_length {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.dataTables_paginate, .dataTables_length, .dataTables_filter {
|
|
||||||
margin: 10px 0!important;
|
|
||||||
}
|
|
|
@ -203,6 +203,9 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.senders-mw220 {
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 350px) {
|
@media (max-width: 350px) {
|
||||||
|
|
|
@ -1,102 +1,104 @@
|
||||||
.pagination a {
|
.pagination a {
|
||||||
text-decoration: none !important;
|
text-decoration: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel.panel-default {
|
.panel.panel-default {
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-responsive {
|
.table-responsive {
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-responsive {
|
.table-responsive {
|
||||||
overflow-x: scroll !important;
|
overflow-x: scroll !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-add-item {
|
.footer-add-item {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background: #F5F5F5;
|
background: #F5F5F5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (min-width: 1920px) {
|
@media (min-width: 1920px) {
|
||||||
.container {
|
.container {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mass-actions-quarantine {
|
.mass-actions-quarantine {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputMissingAttr {
|
.inputMissingAttr {
|
||||||
border-color: #FF4136;
|
border-color: #FF4136;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal#qidDetailModal p {
|
.modal#qidDetailModal p {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
span#qid_detail_score {
|
span#qid_detail_score {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.rspamd-symbol {
|
span.rspamd-symbol {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 2px 6px 2px 0;
|
margin: 2px 6px 2px 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 0 7px;
|
padding: 0 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.rspamd-symbol.positive {
|
span.rspamd-symbol.positive {
|
||||||
background: #4CAF50;
|
background: #4CAF50;
|
||||||
border: 1px solid #4CAF50;
|
border: 1px solid #4CAF50;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.rspamd-symbol.negative {
|
span.rspamd-symbol.negative {
|
||||||
background: #ff4136;
|
background: #ff4136;
|
||||||
border: 1px solid #ff4136;
|
border: 1px solid #ff4136;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.rspamd-symbol.neutral {
|
span.rspamd-symbol.neutral {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
color: #333;
|
color: #333;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.rspamd-symbol span.score {
|
span.rspamd-symbol span.score {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.mail-address-item {
|
span.mail-address-item {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
padding: 2px 7px;
|
padding: 2px 7px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 2px 6px 2px 0;
|
margin: 2px 6px 2px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
table tbody tr {
|
table tbody tr {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
table tbody tr td input[type="checkbox"] {
|
table tbody tr td input[type="checkbox"] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.label-rspamd-action {
|
.label-rspamd-action {
|
||||||
font-size:110%;
|
font-size:110%;
|
||||||
margin:20px;
|
margin:20px;
|
||||||
}
|
}
|
||||||
|
.senders-mw220 {
|
||||||
|
max-width: 220px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,407 +1,468 @@
|
||||||
<?php
|
<?php
|
||||||
function domain_admin($_action, $_data = null) {
|
function domain_admin($_action, $_data = null) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
global $lang;
|
global $lang;
|
||||||
$_data_log = $_data;
|
$_data_log = $_data;
|
||||||
!isset($_data_log['password']) ?: $_data_log['password'] = '*';
|
!isset($_data_log['password']) ?: $_data_log['password'] = '*';
|
||||||
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
|
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
|
||||||
!isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
|
!isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
|
||||||
!isset($_data_log['user_new_pass']) ?: $_data_log['user_new_pass'] = '*';
|
!isset($_data_log['user_new_pass']) ?: $_data_log['user_new_pass'] = '*';
|
||||||
!isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*';
|
!isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*';
|
||||||
switch ($_action) {
|
switch ($_action) {
|
||||||
case 'add':
|
case 'add':
|
||||||
$username = strtolower(trim($_data['username']));
|
$username = strtolower(trim($_data['username']));
|
||||||
$password = $_data['password'];
|
$password = $_data['password'];
|
||||||
$password2 = $_data['password2'];
|
$password2 = $_data['password2'];
|
||||||
$domains = (array)$_data['domains'];
|
$domains = (array)$_data['domains'];
|
||||||
$active = intval($_data['active']);
|
$active = intval($_data['active']);
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => 'access_denied'
|
'msg' => 'access_denied'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (empty($domains)) {
|
if (empty($domains)) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => 'domain_invalid'
|
'msg' => 'domain_invalid'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
|
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('username_invalid', $username)
|
'msg' => array('username_invalid', $username)
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
|
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
|
||||||
WHERE `username` = :username");
|
WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `username` FROM `admin`
|
$stmt = $pdo->prepare("SELECT `username` FROM `admin`
|
||||||
WHERE `username` = :username");
|
WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
|
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
|
||||||
WHERE `username` = :username");
|
WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
|
||||||
foreach ($num_results as $num_results_each) {
|
foreach ($num_results as $num_results_each) {
|
||||||
if ($num_results_each != 0) {
|
if ($num_results_each != 0) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('object_exists', htmlspecialchars($username))
|
'msg' => array('object_exists', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (password_check($password, $password2) !== true) {
|
if (password_check($password, $password2) !== true) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$password_hashed = hash_password($password);
|
$password_hashed = hash_password($password);
|
||||||
$valid_domains = 0;
|
$valid_domains = 0;
|
||||||
foreach ($domains as $domain) {
|
foreach ($domains as $domain) {
|
||||||
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
|
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('domain_invalid', htmlspecialchars($domain))
|
'msg' => array('domain_invalid', htmlspecialchars($domain))
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$valid_domains++;
|
$valid_domains++;
|
||||||
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
|
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
|
||||||
VALUES (:username, :domain, :created, :active)");
|
VALUES (:username, :domain, :created, :active)");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
':domain' => $domain,
|
':domain' => $domain,
|
||||||
':created' => date('Y-m-d H:i:s'),
|
':created' => date('Y-m-d H:i:s'),
|
||||||
':active' => $active
|
':active' => $active
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if ($valid_domains != 0) {
|
if ($valid_domains != 0) {
|
||||||
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
|
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
|
||||||
VALUES (:username, :password_hashed, '0', :active)");
|
VALUES (:username, :password_hashed, '0', :active)");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
':password_hashed' => $password_hashed,
|
':password_hashed' => $password_hashed,
|
||||||
':active' => $active
|
':active' => $active
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)");
|
$stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username
|
':username' => $username
|
||||||
));
|
));
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('domain_admin_added', htmlspecialchars($username))
|
'msg' => array('domain_admin_added', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'edit':
|
case 'edit':
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => 'access_denied'
|
'msg' => 'access_denied'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Administrator
|
// Administrator
|
||||||
if ($_SESSION['mailcow_cc_role'] == "admin") {
|
if ($_SESSION['mailcow_cc_role'] == "admin") {
|
||||||
if (!is_array($_data['username'])) {
|
if (!is_array($_data['username'])) {
|
||||||
$usernames = array();
|
$usernames = array();
|
||||||
$usernames[] = $_data['username'];
|
$usernames[] = $_data['username'];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$usernames = $_data['username'];
|
$usernames = $_data['username'];
|
||||||
}
|
}
|
||||||
foreach ($usernames as $username) {
|
foreach ($usernames as $username) {
|
||||||
$is_now = domain_admin('details', $username);
|
$is_now = domain_admin('details', $username);
|
||||||
$domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null;
|
$domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null;
|
||||||
if (!empty($is_now)) {
|
if (!empty($is_now)) {
|
||||||
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
|
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
|
||||||
$domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
|
$domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
|
||||||
$username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
|
$username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => 'access_denied'
|
'msg' => 'access_denied'
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$password = $_data['password'];
|
$password = $_data['password'];
|
||||||
$password2 = $_data['password2'];
|
$password2 = $_data['password2'];
|
||||||
if (!empty($domains)) {
|
if (!empty($domains)) {
|
||||||
foreach ($domains as $domain) {
|
foreach ($domains as $domain) {
|
||||||
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
|
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('domain_invalid', htmlspecialchars($domain))
|
'msg' => array('domain_invalid', htmlspecialchars($domain))
|
||||||
);
|
);
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
|
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('username_invalid', $username_new)
|
'msg' => array('username_invalid', $username_new)
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($username_new != $username) {
|
if ($username_new != $username) {
|
||||||
if (!empty(domain_admin('details', $username_new)['username'])) {
|
if (!empty(domain_admin('details', $username_new)['username'])) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('username_invalid', $username_new)
|
'msg' => array('username_invalid', $username_new)
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
));
|
));
|
||||||
$stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username_new' => $username_new,
|
':username_new' => $username_new,
|
||||||
':username' => $username
|
':username' => $username
|
||||||
));
|
));
|
||||||
if (!empty($domains)) {
|
if (!empty($domains)) {
|
||||||
foreach ($domains as $domain) {
|
foreach ($domains as $domain) {
|
||||||
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
|
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
|
||||||
VALUES (:username_new, :domain, :created, :active)");
|
VALUES (:username_new, :domain, :created, :active)");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username_new' => $username_new,
|
':username_new' => $username_new,
|
||||||
':domain' => $domain,
|
':domain' => $domain,
|
||||||
':created' => date('Y-m-d H:i:s'),
|
':created' => date('Y-m-d H:i:s'),
|
||||||
':active' => $active
|
':active' => $active
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!empty($password)) {
|
if (!empty($password)) {
|
||||||
if (password_check($password, $password2) !== true) {
|
if (password_check($password, $password2) !== true) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$password_hashed = hash_password($password);
|
$password_hashed = hash_password($password);
|
||||||
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':password_hashed' => $password_hashed,
|
':password_hashed' => $password_hashed,
|
||||||
':username_new' => $username_new,
|
':username_new' => $username_new,
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
':active' => $active
|
':active' => $active
|
||||||
));
|
));
|
||||||
if (isset($_data['disable_tfa'])) {
|
if (isset($_data['disable_tfa'])) {
|
||||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
|
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username_new' => $username_new,
|
':username_new' => $username_new,
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
':active' => $active
|
':active' => $active
|
||||||
));
|
));
|
||||||
if (isset($_data['disable_tfa'])) {
|
if (isset($_data['disable_tfa'])) {
|
||||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
|
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('domain_admin_modified', htmlspecialchars($username))
|
'msg' => array('domain_admin_modified', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Domain administrator
|
// Domain administrator
|
||||||
// Can only edit itself
|
// Can only edit itself
|
||||||
elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
|
||||||
$username = $_SESSION['mailcow_cc_username'];
|
$username = $_SESSION['mailcow_cc_username'];
|
||||||
$password_old = $_data['user_old_pass'];
|
$password_old = $_data['user_old_pass'];
|
||||||
$password_new = $_data['user_new_pass'];
|
$password_new = $_data['user_new_pass'];
|
||||||
$password_new2 = $_data['user_new_pass2'];
|
$password_new2 = $_data['user_new_pass2'];
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
|
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
|
||||||
WHERE `username` = :user");
|
WHERE `username` = :user");
|
||||||
$stmt->execute(array(':user' => $username));
|
$stmt->execute(array(':user' => $username));
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if (!verify_hash($row['password'], $password_old)) {
|
if (!verify_hash($row['password'], $password_old)) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => 'access_denied'
|
'msg' => 'access_denied'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (password_check($password_new, $password_new2) !== true) {
|
if (password_check($password_new, $password_new2) !== true) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$password_hashed = hash_password($password_new);
|
$password_hashed = hash_password($password_new);
|
||||||
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':password_hashed' => $password_hashed,
|
':password_hashed' => $password_hashed,
|
||||||
':username' => $username
|
':username' => $username
|
||||||
));
|
));
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('domain_admin_modified', htmlspecialchars($username))
|
'msg' => array('domain_admin_modified', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => 'access_denied'
|
'msg' => 'access_denied'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$usernames = (array)$_data['username'];
|
$usernames = (array)$_data['username'];
|
||||||
foreach ($usernames as $username) {
|
foreach ($usernames as $username) {
|
||||||
if (empty(domain_admin('details', $username))) {
|
if (empty(domain_admin('details', $username))) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('username_invalid', $username)
|
'msg' => array('username_invalid', $username)
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
));
|
));
|
||||||
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
));
|
));
|
||||||
$stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
));
|
));
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
));
|
));
|
||||||
$stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
|
$stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
));
|
));
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => array('domain_admin_removed', htmlspecialchars($username))
|
'msg' => array('domain_admin_removed', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'get':
|
case 'get':
|
||||||
$domainadmins = array();
|
$domainadmins = array();
|
||||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $_action, $_data_log),
|
'log' => array(__FUNCTION__, $_action, $_data_log),
|
||||||
'msg' => 'access_denied'
|
'msg' => 'access_denied'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$stmt = $pdo->query("SELECT DISTINCT
|
$stmt = $pdo->query("SELECT DISTINCT
|
||||||
`username`
|
`username`
|
||||||
FROM `domain_admins`
|
FROM `domain_admins`
|
||||||
WHERE `username` IN (
|
WHERE `username` IN (
|
||||||
SELECT `username` FROM `admin`
|
SELECT `username` FROM `admin`
|
||||||
WHERE `superadmin`!='1'
|
WHERE `superadmin`!='1'
|
||||||
)");
|
)");
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
while ($row = array_shift($rows)) {
|
while ($row = array_shift($rows)) {
|
||||||
$domainadmins[] = $row['username'];
|
$domainadmins[] = $row['username'];
|
||||||
}
|
}
|
||||||
return $domainadmins;
|
return $domainadmins;
|
||||||
break;
|
break;
|
||||||
case 'details':
|
case 'details':
|
||||||
$domainadmindata = array();
|
$domainadmindata = array();
|
||||||
if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
|
if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
|
elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
|
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$stmt = $pdo->prepare("SELECT
|
$stmt = $pdo->prepare("SELECT
|
||||||
`tfa`.`active` AS `tfa_active`,
|
`tfa`.`active` AS `tfa_active`,
|
||||||
`domain_admins`.`username`,
|
`domain_admins`.`username`,
|
||||||
`domain_admins`.`created`,
|
`domain_admins`.`created`,
|
||||||
`domain_admins`.`active` AS `active`
|
`domain_admins`.`active` AS `active`
|
||||||
FROM `domain_admins`
|
FROM `domain_admins`
|
||||||
LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
|
LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
|
||||||
WHERE `domain_admins`.`username`= :domain_admin");
|
WHERE `domain_admins`.`username`= :domain_admin");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':domain_admin' => $_data
|
':domain_admin' => $_data
|
||||||
));
|
));
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if (empty($row)) {
|
if (empty($row)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$domainadmindata['username'] = $row['username'];
|
$domainadmindata['username'] = $row['username'];
|
||||||
$domainadmindata['tfa_active'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
|
$domainadmindata['tfa_active'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
|
||||||
$domainadmindata['tfa_active_int'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
|
$domainadmindata['tfa_active_int'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
|
||||||
$domainadmindata['active'] = $row['active'];
|
$domainadmindata['active'] = $row['active'];
|
||||||
$domainadmindata['active_int'] = $row['active'];
|
$domainadmindata['active_int'] = $row['active'];
|
||||||
$domainadmindata['created'] = $row['created'];
|
$domainadmindata['created'] = $row['created'];
|
||||||
// GET SELECTED
|
// GET SELECTED
|
||||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||||
WHERE `domain` IN (
|
WHERE `domain` IN (
|
||||||
SELECT `domain` FROM `domain_admins`
|
SELECT `domain` FROM `domain_admins`
|
||||||
WHERE `username`= :domain_admin)");
|
WHERE `username`= :domain_admin)");
|
||||||
$stmt->execute(array(':domain_admin' => $_data));
|
$stmt->execute(array(':domain_admin' => $_data));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
while($row = array_shift($rows)) {
|
while($row = array_shift($rows)) {
|
||||||
$domainadmindata['selected_domains'][] = $row['domain'];
|
$domainadmindata['selected_domains'][] = $row['domain'];
|
||||||
}
|
}
|
||||||
// GET UNSELECTED
|
// GET UNSELECTED
|
||||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||||
WHERE `domain` NOT IN (
|
WHERE `domain` NOT IN (
|
||||||
SELECT `domain` FROM `domain_admins`
|
SELECT `domain` FROM `domain_admins`
|
||||||
WHERE `username`= :domain_admin)");
|
WHERE `username`= :domain_admin)");
|
||||||
$stmt->execute(array(':domain_admin' => $_data));
|
$stmt->execute(array(':domain_admin' => $_data));
|
||||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
while($row = array_shift($rows)) {
|
while($row = array_shift($rows)) {
|
||||||
$domainadmindata['unselected_domains'][] = $row['domain'];
|
$domainadmindata['unselected_domains'][] = $row['domain'];
|
||||||
}
|
}
|
||||||
if (!isset($domainadmindata['unselected_domains'])) {
|
if (!isset($domainadmindata['unselected_domains'])) {
|
||||||
$domainadmindata['unselected_domains'] = "";
|
$domainadmindata['unselected_domains'] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $domainadmindata;
|
return $domainadmindata;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function domain_admin_sso($_action, $_data) {
|
||||||
|
global $pdo;
|
||||||
|
|
||||||
|
switch ($_action) {
|
||||||
|
case 'check':
|
||||||
|
$token = $_data;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT `t1`.`username` FROM `da_sso` AS `t1` JOIN `admin` AS `t2` ON `t1`.`username` = `t2`.`username` WHERE `t1`.`token` = :token AND `t1`.`created` > DATE_SUB(NOW(), INTERVAL '30' SECOND) AND `t2`.`active` = 1 AND `t2`.`superadmin` = 0;");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':token' => preg_replace('/[^a-zA-Z0-9-]/', '', $token)
|
||||||
|
));
|
||||||
|
$return = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
return empty($return['username']) ? false : $return['username'];
|
||||||
|
case 'issue':
|
||||||
|
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_data),
|
||||||
|
'msg' => 'access_denied'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$username = $_data['username'];
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
|
||||||
|
WHERE `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
|
||||||
|
if ($num_results < 1) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $_action, $_data),
|
||||||
|
'msg' => array('object_doesnt_exist', htmlspecialchars($username))
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = implode('-', array(
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3))),
|
||||||
|
strtoupper(bin2hex(random_bytes(3)))
|
||||||
|
));
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO `da_sso` (`username`, `token`)
|
||||||
|
VALUES (:username, :token)");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
':token' => $token
|
||||||
|
));
|
||||||
|
|
||||||
|
// perform cleanup
|
||||||
|
$pdo->query("DELETE FROM `da_sso` WHERE created < DATE_SUB(NOW(), INTERVAL '30' SECOND);");
|
||||||
|
|
||||||
|
return ['token' => $token];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1739,7 +1739,7 @@ function verify_tfa_login($username, $_data) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
'msg' => array('webauthn_verification_failed', 'authenticator not found')
|
'msg' => array('webauthn_authenticator_failed')
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1748,11 +1748,20 @@ function verify_tfa_login($username, $_data) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
'msg' => array('webauthn_verification_failed', 'publicKey not found')
|
'msg' => array('webauthn_publickey_failed')
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
|
'msg' => array('webauthn_username_failed')
|
||||||
|
);
|
||||||
|
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']);
|
||||||
}
|
}
|
||||||
|
@ -1784,21 +1793,12 @@ function verify_tfa_login($username, $_data) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
'log' => array(__FUNCTION__, $username, '*'),
|
||||||
'msg' => array('webauthn_verification_failed', 'could not determine user role')
|
'msg' => array('webauthn_role_failed')
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'danger',
|
|
||||||
'log' => array(__FUNCTION__, $username, '*'),
|
|
||||||
'msg' => array('webauthn_verification_failed', 'user who requests does not match with sql entry')
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
||||||
$_SESSION['tfa_id'] = $process_webauthn['id'];
|
$_SESSION['tfa_id'] = $process_webauthn['id'];
|
||||||
$_SESSION['authReq'] = null;
|
$_SESSION['authReq'] = null;
|
||||||
|
|
|
@ -5264,7 +5264,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'mailbox', 'resource'))) {
|
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'mailbox', 'resource')) && getenv('SKIP_SOGO') != "y") {
|
||||||
update_sogo_static_view();
|
update_sogo_static_view();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,140 +1,140 @@
|
||||||
<?php
|
<?php
|
||||||
// Start session
|
// Start session
|
||||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
ini_set("session.cookie_httponly", 1);
|
ini_set("session.cookie_httponly", 1);
|
||||||
ini_set('session.gc_maxlifetime', $SESSION_LIFETIME);
|
ini_set('session.gc_maxlifetime', $SESSION_LIFETIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
|
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
|
||||||
strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == "https") {
|
strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == "https") {
|
||||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
ini_set("session.cookie_secure", 1);
|
ini_set("session.cookie_secure", 1);
|
||||||
}
|
}
|
||||||
$IS_HTTPS = true;
|
$IS_HTTPS = true;
|
||||||
}
|
}
|
||||||
elseif (isset($_SERVER['HTTPS'])) {
|
elseif (isset($_SERVER['HTTPS'])) {
|
||||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
ini_set("session.cookie_secure", 1);
|
ini_set("session.cookie_secure", 1);
|
||||||
}
|
}
|
||||||
$IS_HTTPS = true;
|
$IS_HTTPS = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$IS_HTTPS = false;
|
$IS_HTTPS = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($_SESSION['CSRF']['TOKEN'])) {
|
if (!isset($_SESSION['CSRF']['TOKEN'])) {
|
||||||
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
|
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set session UA
|
// Set session UA
|
||||||
if (!isset($_SESSION['SESS_REMOTE_UA'])) {
|
if (!isset($_SESSION['SESS_REMOTE_UA'])) {
|
||||||
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
|
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep session active
|
// Keep session active
|
||||||
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $SESSION_LIFETIME)) {
|
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $SESSION_LIFETIME)) {
|
||||||
session_unset();
|
session_unset();
|
||||||
session_destroy();
|
session_destroy();
|
||||||
}
|
}
|
||||||
$_SESSION['LAST_ACTIVITY'] = time();
|
$_SESSION['LAST_ACTIVITY'] = time();
|
||||||
|
|
||||||
// API
|
// API
|
||||||
if (!empty($_SERVER['HTTP_X_API_KEY'])) {
|
if (!empty($_SERVER['HTTP_X_API_KEY'])) {
|
||||||
$stmt = $pdo->prepare("SELECT * FROM `api` WHERE `api_key` = :api_key AND `active` = '1';");
|
$stmt = $pdo->prepare("SELECT * FROM `api` WHERE `api_key` = :api_key AND `active` = '1';");
|
||||||
$stmt->execute(array(
|
$stmt->execute(array(
|
||||||
':api_key' => preg_replace('/[^a-zA-Z0-9-]/', '', $_SERVER['HTTP_X_API_KEY'])
|
':api_key' => preg_replace('/[^a-zA-Z0-9-]/', '', $_SERVER['HTTP_X_API_KEY'])
|
||||||
));
|
));
|
||||||
$api_return = $stmt->fetch(PDO::FETCH_ASSOC);
|
$api_return = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if (!empty($api_return['api_key'])) {
|
if (!empty($api_return['api_key'])) {
|
||||||
$skip_ip_check = ($api_return['skip_ip_check'] == 1);
|
$skip_ip_check = ($api_return['skip_ip_check'] == 1);
|
||||||
$remote = get_remote_ip(false);
|
$remote = get_remote_ip(false);
|
||||||
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from']));
|
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from']));
|
||||||
if ($skip_ip_check === true || ip_acl($remote, $allow_from)) {
|
if ($skip_ip_check === true || ip_acl($remote, $allow_from)) {
|
||||||
$_SESSION['mailcow_cc_username'] = 'API';
|
$_SESSION['mailcow_cc_username'] = 'API';
|
||||||
$_SESSION['mailcow_cc_role'] = 'admin';
|
$_SESSION['mailcow_cc_role'] = 'admin';
|
||||||
$_SESSION['mailcow_cc_api'] = true;
|
$_SESSION['mailcow_cc_api'] = true;
|
||||||
if ($api_return['access'] == 'rw') {
|
if ($api_return['access'] == 'rw') {
|
||||||
$_SESSION['mailcow_cc_api_access'] = 'rw';
|
$_SESSION['mailcow_cc_api_access'] = 'rw';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$_SESSION['mailcow_cc_api_access'] = 'ro';
|
$_SESSION['mailcow_cc_api_access'] = 'ro';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
|
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
|
||||||
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
|
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
|
||||||
http_response_code(401);
|
http_response_code(401);
|
||||||
echo json_encode(array(
|
echo json_encode(array(
|
||||||
'type' => 'error',
|
'type' => 'error',
|
||||||
'msg' => 'api access denied for ip ' . $_SERVER['REMOTE_ADDR']
|
'msg' => 'api access denied for ip ' . $_SERVER['REMOTE_ADDR']
|
||||||
));
|
));
|
||||||
unset($_POST);
|
unset($_POST);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
|
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
|
||||||
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
|
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
|
||||||
http_response_code(401);
|
http_response_code(401);
|
||||||
echo json_encode(array(
|
echo json_encode(array(
|
||||||
'type' => 'error',
|
'type' => 'error',
|
||||||
'msg' => 'authentication failed'
|
'msg' => 'authentication failed'
|
||||||
));
|
));
|
||||||
unset($_POST);
|
unset($_POST);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle logouts
|
// Handle logouts
|
||||||
if (isset($_POST["logout"])) {
|
if (isset($_POST["logout"])) {
|
||||||
if (isset($_SESSION["dual-login"])) {
|
if (isset($_SESSION["dual-login"])) {
|
||||||
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
|
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
|
||||||
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
|
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
|
||||||
unset($_SESSION["dual-login"]);
|
unset($_SESSION["dual-login"]);
|
||||||
header("Location: /mailbox");
|
header("Location: /mailbox");
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
session_regenerate_id(true);
|
session_regenerate_id(true);
|
||||||
session_unset();
|
session_unset();
|
||||||
session_destroy();
|
session_destroy();
|
||||||
session_write_close();
|
session_write_close();
|
||||||
header("Location: /");
|
header("Location: /");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check session
|
// Check session
|
||||||
function session_check() {
|
function session_check() {
|
||||||
if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) {
|
if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!isset($_SESSION['SESS_REMOTE_UA']) || ($_SESSION['SESS_REMOTE_UA'] != $_SERVER['HTTP_USER_AGENT'])) {
|
if (!isset($_SESSION['SESS_REMOTE_UA']) || ($_SESSION['SESS_REMOTE_UA'] != $_SERVER['HTTP_USER_AGENT'])) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'warning',
|
'type' => 'warning',
|
||||||
'msg' => 'session_ua'
|
'msg' => 'session_ua'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!empty($_POST)) {
|
if (!empty($_POST)) {
|
||||||
if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) {
|
if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) {
|
||||||
$_SESSION['return'][] = array(
|
$_SESSION['return'][] = array(
|
||||||
'type' => 'warning',
|
'type' => 'warning',
|
||||||
'msg' => 'session_token'
|
'msg' => 'session_token'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
unset($_POST['csrf_token']);
|
unset($_POST['csrf_token']);
|
||||||
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
|
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
|
||||||
$_SESSION['CSRF']['TIME'] = time();
|
$_SESSION['CSRF']['TIME'] = time();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_SESSION['mailcow_cc_role']) && session_check() === false) {
|
if (isset($_SESSION['mailcow_cc_role']) && session_check() === false) {
|
||||||
$_POST = array();
|
$_POST = array();
|
||||||
$_FILES = array();
|
$_FILES = array();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
|
// SSO Domain Admin
|
||||||
|
if (!empty($_GET['sso_token'])) {
|
||||||
|
$username = domain_admin_sso('check', $_GET['sso_token']);
|
||||||
|
|
||||||
|
if ($username !== false) {
|
||||||
|
$_SESSION['mailcow_cc_username'] = $username;
|
||||||
|
$_SESSION['mailcow_cc_role'] = 'domainadmin';
|
||||||
|
header('Location: /mailbox');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($_POST["verify_tfa_login"])) {
|
if (isset($_POST["verify_tfa_login"])) {
|
||||||
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) {
|
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'];
|
||||||
|
@ -6,7 +17,7 @@ if (isset($_POST["verify_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_methods']);
|
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']);
|
||||||
|
@ -34,7 +45,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";
|
||||||
|
|
|
@ -124,7 +124,7 @@ $MAILCOW_APPS = array(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rows until pagination begins
|
// Rows until pagination begins
|
||||||
$PAGINATION_SIZE = 20;
|
$PAGINATION_SIZE = 25;
|
||||||
|
|
||||||
// Default number of rows/lines to display (log table)
|
// Default number of rows/lines to display (log table)
|
||||||
$LOG_LINES = 1000;
|
$LOG_LINES = 1000;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -34,7 +34,7 @@ $(document).ready(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// set update loop container list
|
// set update loop container list
|
||||||
containersToUpdate = {}
|
containersToUpdate = {};
|
||||||
// set default ChartJs Font Color
|
// set default ChartJs Font Color
|
||||||
Chart.defaults.color = '#999';
|
Chart.defaults.color = '#999';
|
||||||
// create host cpu and mem charts
|
// create host cpu and mem charts
|
||||||
|
@ -44,14 +44,13 @@ $(document).ready(function() {
|
||||||
check_update(mailcow_info.version_tag, mailcow_info.project_url);
|
check_update(mailcow_info.version_tag, mailcow_info.project_url);
|
||||||
}
|
}
|
||||||
$("#maiclow_version").click(function(){
|
$("#maiclow_version").click(function(){
|
||||||
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" ||
|
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
|
||||||
mailcow_info.branch !== "master")
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag);
|
showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag);
|
||||||
})
|
})
|
||||||
// get public ips
|
// get public ips
|
||||||
$("#host_show_ip").click(function(){
|
$("#host_show_ip").click(function(){
|
||||||
$("#host_show_ip").find(".text").addClass("d-none");
|
$("#host_show_ip").find(".text").addClass("d-none");
|
||||||
$("#host_show_ip").find(".spinner-border").removeClass("d-none");
|
$("#host_show_ip").find(".spinner-border").removeClass("d-none");
|
||||||
|
|
||||||
|
@ -76,7 +75,7 @@ $(document).ready(function() {
|
||||||
$("#host_ipv6").addClass("d-block");
|
$("#host_ipv6").addClass("d-block");
|
||||||
}).catch(function(error){
|
}).catch(function(error){
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
||||||
$("#host_ipv6").removeClass("d-none");
|
$("#host_ipv6").removeClass("d-none");
|
||||||
$("#host_ipv6").addClass("d-block");
|
$("#host_ipv6").addClass("d-block");
|
||||||
$("#host_ipv6").addClass("text-danger");
|
$("#host_ipv6").addClass("text-danger");
|
||||||
|
@ -119,10 +118,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#autodiscover_log').DataTable({
|
var table = $('#autodiscover_log').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -188,10 +188,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#postfix_log').DataTable({
|
var table = $('#postfix_log').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -242,10 +243,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#watchdog_log').DataTable({
|
var table = $('#watchdog_log').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -300,10 +302,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#api_log').DataTable({
|
var table = $('#api_log').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -352,7 +355,7 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on('responsive-resize', function (e, datatable, columns){
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
|
hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
|
||||||
});
|
});
|
||||||
|
@ -365,10 +368,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#rl_log').DataTable({
|
var table = $('#rl_log').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -455,7 +459,7 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on('responsive-resize', function (e, datatable, columns){
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
|
hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
|
||||||
});
|
});
|
||||||
|
@ -468,10 +472,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#ui_logs').DataTable({
|
var table = $('#ui_logs').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -538,7 +543,7 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on('responsive-resize', function (e, datatable, columns){
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_log');
|
hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_log');
|
||||||
});
|
});
|
||||||
|
@ -551,10 +556,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#sasl_logs').DataTable({
|
var table = $('#sasl_logs').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -598,7 +604,7 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on('responsive-resize', function (e, datatable, columns){
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
|
hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
|
||||||
});
|
});
|
||||||
|
@ -611,10 +617,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#acme_log').DataTable({
|
var table = $('#acme_log').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -647,7 +654,7 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on('responsive-resize', function (e, datatable, columns){
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
|
hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
|
||||||
});
|
});
|
||||||
|
@ -660,10 +667,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#netfilter_log').DataTable({
|
var table = $('#netfilter_log').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -701,7 +709,7 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on('responsive-resize', function (e, datatable, columns){
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
|
hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
|
||||||
});
|
});
|
||||||
|
@ -714,10 +722,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#sogo_log').DataTable({
|
var table = $('#sogo_log').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -755,7 +764,7 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on('responsive-resize', function (e, datatable, columns){
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
|
hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
|
||||||
});
|
});
|
||||||
|
@ -768,10 +777,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#dovecot_log').DataTable({
|
var table = $('#dovecot_log').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -883,10 +893,11 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
|
|
||||||
var table = $('#rspamd_history').DataTable({
|
var table = $('#rspamd_history').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
|
pageLength: log_pagination_size,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"tr" +
|
"tr" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
|
@ -983,7 +994,7 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on('responsive-resize', function (e, datatable, columns){
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
hideTableExpandCollapseBtn('#tab-rspamd-history', '#rspamd_history');
|
hideTableExpandCollapseBtn('#tab-rspamd-history', '#rspamd_history');
|
||||||
});
|
});
|
||||||
|
@ -998,31 +1009,31 @@ jQuery(function($){
|
||||||
item.rcpt = escapeHtml(item.rcpt_smtp.join(", "));
|
item.rcpt = escapeHtml(item.rcpt_smtp.join(", "));
|
||||||
}
|
}
|
||||||
item.symbols = Object.keys(item.symbols).sort(function (a, b) {
|
item.symbols = Object.keys(item.symbols).sort(function (a, b) {
|
||||||
if (item.symbols[a].score === 0) return 1
|
if (item.symbols[a].score === 0) return 1;
|
||||||
if (item.symbols[b].score === 0) return -1
|
if (item.symbols[b].score === 0) return -1;
|
||||||
if (item.symbols[b].score < 0 && item.symbols[a].score < 0) {
|
if (item.symbols[b].score < 0 && item.symbols[a].score < 0) {
|
||||||
return item.symbols[a].score - item.symbols[b].score
|
return item.symbols[a].score - item.symbols[b].score;
|
||||||
}
|
}
|
||||||
if (item.symbols[b].score > 0 && item.symbols[a].score > 0) {
|
if (item.symbols[b].score > 0 && item.symbols[a].score > 0) {
|
||||||
return item.symbols[b].score - item.symbols[a].score
|
return item.symbols[b].score - item.symbols[a].score;
|
||||||
}
|
}
|
||||||
return item.symbols[b].score - item.symbols[a].score
|
return item.symbols[b].score - item.symbols[a].score;
|
||||||
}).map(function(key) {
|
}).map(function(key) {
|
||||||
var sym = item.symbols[key];
|
var sym = item.symbols[key];
|
||||||
if (sym.score < 0) {
|
if (sym.score < 0) {
|
||||||
sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)'
|
sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)';
|
||||||
}
|
}
|
||||||
else if (sym.score === 0) {
|
else if (sym.score === 0) {
|
||||||
sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)'
|
sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)'
|
sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)';
|
||||||
}
|
}
|
||||||
var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
|
var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
|
||||||
if (sym.options) {
|
if (sym.options) {
|
||||||
str += ' [' + escapeHtml(sym.options.join(", ")) + "]";
|
str += ' [' + escapeHtml(sym.options.join(", ")) + "]";
|
||||||
}
|
}
|
||||||
return str
|
return str;
|
||||||
}).join('<br>\n');
|
}).join('<br>\n');
|
||||||
item.subject = escapeHtml(item.subject);
|
item.subject = escapeHtml(item.subject);
|
||||||
var scan_time = item.time_real.toFixed(3);
|
var scan_time = item.time_real.toFixed(3);
|
||||||
|
@ -1155,14 +1166,14 @@ jQuery(function($){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return data
|
return data;
|
||||||
};
|
};
|
||||||
$('.add_log_lines').on('click', function (e) {
|
$('.add_log_lines').on('click', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var log_table= $(this).data("table")
|
var log_table= $(this).data("table");
|
||||||
var new_nrows = $(this).data("nrows")
|
var new_nrows = $(this).data("nrows");
|
||||||
var post_process = $(this).data("post-process")
|
var post_process = $(this).data("post-process");
|
||||||
var log_url = $(this).data("log-url")
|
var log_url = $(this).data("log-url");
|
||||||
if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
|
if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
|
||||||
console.log("no data-table or data-nrows or log_url or data-post-process attr found");
|
console.log("no data-table or data-nrows or log_url or data-post-process attr found");
|
||||||
return;
|
return;
|
||||||
|
@ -1184,9 +1195,9 @@ jQuery(function($){
|
||||||
})
|
})
|
||||||
function hideTableExpandCollapseBtn(tab, table){
|
function hideTableExpandCollapseBtn(tab, table){
|
||||||
if ($(table).hasClass('collapsed'))
|
if ($(table).hasClass('collapsed'))
|
||||||
$(tab).find(".table_collapse_option").show();
|
$(tab).find(".table_collapse_option").show();
|
||||||
else
|
else
|
||||||
$(tab).find(".table_collapse_option").hide();
|
$(tab).find(".table_collapse_option").hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect element visibility changes
|
// detect element visibility changes
|
||||||
|
@ -1220,7 +1231,6 @@ jQuery(function($){
|
||||||
onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
|
onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// start polling host stats if tab is active
|
// start polling host stats if tab is active
|
||||||
onVisible("[id^=tab-containers]", () => update_stats());
|
onVisible("[id^=tab-containers]", () => update_stats());
|
||||||
// start polling container stats if collapse is active
|
// start polling container stats if collapse is active
|
||||||
|
@ -1303,9 +1313,9 @@ function update_stats(timeout=5){
|
||||||
if (mem_chart.data.labels.length > 30) mem_chart.data.labels.shift();
|
if (mem_chart.data.labels.length > 30) mem_chart.data.labels.shift();
|
||||||
|
|
||||||
cpu_chart.data.datasets[0].data.push(data.cpu.usage);
|
cpu_chart.data.datasets[0].data.push(data.cpu.usage);
|
||||||
if (cpu_chart.data.datasets[0].data.length > 30) cpu_chart.data.datasets[0].data.shift();
|
if (cpu_chart.data.datasets[0].data.length > 30) cpu_chart.data.datasets[0].data.shift();
|
||||||
mem_chart.data.datasets[0].data.push(data.memory.usage);
|
mem_chart.data.datasets[0].data.push(data.memory.usage);
|
||||||
if (mem_chart.data.datasets[0].data.length > 30) mem_chart.data.datasets[0].data.shift();
|
if (mem_chart.data.datasets[0].data.length > 30) mem_chart.data.datasets[0].data.shift();
|
||||||
|
|
||||||
cpu_chart.update();
|
cpu_chart.update();
|
||||||
mem_chart.update();
|
mem_chart.update();
|
||||||
|
@ -1464,23 +1474,23 @@ function createReadWriteChart(chart_id, read_lable, write_lable){
|
||||||
};
|
};
|
||||||
var optionsNet = {
|
var optionsNet = {
|
||||||
interaction: {
|
interaction: {
|
||||||
mode: 'index'
|
mode: 'index'
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
yAxis: {
|
yAxis: {
|
||||||
min: 0,
|
min: 0,
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function(i, index, ticks) {
|
callback: function(i, index, ticks) {
|
||||||
return formatBytes(i);
|
return formatBytes(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1528,13 +1538,13 @@ function createHostCpuAndMemChart(){
|
||||||
};
|
};
|
||||||
var optionsCpu = {
|
var optionsCpu = {
|
||||||
interaction: {
|
interaction: {
|
||||||
mode: 'index'
|
mode: 'index'
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
yAxis: {
|
yAxis: {
|
||||||
min: 0,
|
min: 0,
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function(i, index, ticks) {
|
callback: function(i, index, ticks) {
|
||||||
|
@ -1544,7 +1554,7 @@ function createHostCpuAndMemChart(){
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1566,13 +1576,13 @@ function createHostCpuAndMemChart(){
|
||||||
};
|
};
|
||||||
var optionsMem = {
|
var optionsMem = {
|
||||||
interaction: {
|
interaction: {
|
||||||
mode: 'index'
|
mode: 'index'
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
yAxis: {
|
yAxis: {
|
||||||
min: 0,
|
min: 0,
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function(i, index, ticks) {
|
callback: function(i, index, ticks) {
|
||||||
|
@ -1582,7 +1592,7 @@ function createHostCpuAndMemChart(){
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1678,22 +1688,22 @@ function parseGithubMarkdownLinks(inputText) {
|
||||||
|
|
||||||
replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
||||||
replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
|
replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
|
||||||
if (matched.includes('github.com')){
|
if (matched.includes('github.com')){
|
||||||
// return short link if it's github link
|
// return short link if it's github link
|
||||||
last_uri_path = matched.split('/');
|
last_uri_path = matched.split('/');
|
||||||
last_uri_path = last_uri_path[last_uri_path.length - 1];
|
last_uri_path = last_uri_path[last_uri_path.length - 1];
|
||||||
|
|
||||||
// adjust Full Changelog link to match last git version and new git version, if link is a compare link
|
// adjust Full Changelog link to match last git version and new git version, if link is a compare link
|
||||||
if (matched.includes('/compare/') && mailcow_info.last_version_tag !== ''){
|
if (matched.includes('/compare/') && mailcow_info.last_version_tag !== ''){
|
||||||
matched = matched.replace(last_uri_path, mailcow_info.last_version_tag + '...' + mailcow_info.version_tag);
|
matched = matched.replace(last_uri_path, mailcow_info.last_version_tag + '...' + mailcow_info.version_tag);
|
||||||
last_uri_path = mailcow_info.last_version_tag + '...' + mailcow_info.version_tag;
|
last_uri_path = mailcow_info.last_version_tag + '...' + mailcow_info.version_tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '<a href="' + matched + '" target="_blank">' + last_uri_path + '</a><br>';
|
return '<a href="' + matched + '" target="_blank">' + last_uri_path + '</a><br>';
|
||||||
};
|
};
|
||||||
|
|
||||||
// if it's not a github link, return complete link
|
// if it's not a github link, return complete link
|
||||||
return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
|
return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
|
||||||
});
|
});
|
||||||
|
|
||||||
return replacedText;
|
return replacedText;
|
||||||
|
|
|
@ -1,220 +1,222 @@
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
|
$(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
|
||||||
$("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
|
$("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
|
||||||
$(".goto_checkbox").click(function( event ) {
|
$(".goto_checkbox").click(function( event ) {
|
||||||
$("form[data-id='editalias'] .goto_checkbox").not(this).prop('checked', false);
|
$("form[data-id='editalias'] .goto_checkbox").not(this).prop('checked', false);
|
||||||
if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
|
if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
|
||||||
$('#textarea_alias_goto').prop('disabled', true);
|
$('#textarea_alias_goto').prop('disabled', true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$("#textarea_alias_goto").removeAttr('disabled');
|
$("#textarea_alias_goto").removeAttr('disabled');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$("#disable_sender_check").click(function( event ) {
|
$("#disable_sender_check").click(function( event ) {
|
||||||
if ($("form[data-id='editmailbox'] #disable_sender_check:checked").length > 0) {
|
if ($("form[data-id='editmailbox'] #disable_sender_check:checked").length > 0) {
|
||||||
$('#editSelectSenderACL').prop('disabled', true);
|
$('#editSelectSenderACL').prop('disabled', true);
|
||||||
$('#editSelectSenderACL').selectpicker('refresh');
|
$('#editSelectSenderACL').selectpicker('refresh');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$('#editSelectSenderACL').prop('disabled', false);
|
$('#editSelectSenderACL').prop('disabled', false);
|
||||||
$('#editSelectSenderACL').selectpicker('refresh');
|
$('#editSelectSenderACL').selectpicker('refresh');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
|
if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
|
||||||
$('#textarea_alias_goto').prop('disabled', true);
|
$('#textarea_alias_goto').prop('disabled', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#mailbox-password-warning-close").click(function( event ) {
|
$("#mailbox-password-warning-close").click(function( event ) {
|
||||||
$('#mailbox-passwd-hidden-info').addClass('hidden');
|
$('#mailbox-passwd-hidden-info').addClass('hidden');
|
||||||
$('#mailbox-passwd-form-groups').removeClass('hidden');
|
$('#mailbox-passwd-form-groups').removeClass('hidden');
|
||||||
});
|
});
|
||||||
// Sender ACL
|
// Sender ACL
|
||||||
if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
|
if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
|
||||||
$("#sender_acl_disabled").show();
|
$("#sender_acl_disabled").show();
|
||||||
}
|
}
|
||||||
$('#editSelectSenderACL').change(function() {
|
$('#editSelectSenderACL').change(function() {
|
||||||
if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
|
if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
|
||||||
$("#sender_acl_disabled").show();
|
$("#sender_acl_disabled").show();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$("#sender_acl_disabled").hide();
|
$("#sender_acl_disabled").hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Resources
|
// Resources
|
||||||
if ($("#editSelectMultipleBookings").val() == "custom") {
|
if ($("#editSelectMultipleBookings").val() == "custom") {
|
||||||
$("#multiple_bookings_custom_div").show();
|
$("#multiple_bookings_custom_div").show();
|
||||||
$('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
|
$('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
|
||||||
}
|
}
|
||||||
$("#editSelectMultipleBookings").change(function() {
|
$("#editSelectMultipleBookings").change(function() {
|
||||||
$('input[name=multiple_bookings]').val($("#editSelectMultipleBookings").val());
|
$('input[name=multiple_bookings]').val($("#editSelectMultipleBookings").val());
|
||||||
if ($('input[name=multiple_bookings]').val() == "custom") {
|
if ($('input[name=multiple_bookings]').val() == "custom") {
|
||||||
$("#multiple_bookings_custom_div").show();
|
$("#multiple_bookings_custom_div").show();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$("#multiple_bookings_custom_div").hide();
|
$("#multiple_bookings_custom_div").hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$("#multiple_bookings_custom").bind("change keypress keyup blur", function() {
|
$("#multiple_bookings_custom").bind("change keypress keyup blur", function() {
|
||||||
$('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
|
$('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
|
||||||
});
|
});
|
||||||
|
|
||||||
// load tags
|
// load tags
|
||||||
if ($('#tags').length){
|
if ($('#tags').length){
|
||||||
var tagsEl = $('#tags').parent().find('.tag-values')[0];
|
var tagsEl = $('#tags').parent().find('.tag-values')[0];
|
||||||
console.log($(tagsEl).val())
|
console.log($(tagsEl).val())
|
||||||
var tags = JSON.parse($(tagsEl).val());
|
var tags = JSON.parse($(tagsEl).val());
|
||||||
$(tagsEl).val("");
|
$(tagsEl).val("");
|
||||||
|
|
||||||
for (var i = 0; i < tags.length; i++)
|
for (var i = 0; i < tags.length; i++)
|
||||||
addTag($('#tags'), tags[i]);
|
addTag($('#tags'), tags[i]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
jQuery(function($){
|
jQuery(function($){
|
||||||
// http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
|
// http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
|
||||||
function validateEmail(email) {
|
function validateEmail(email) {
|
||||||
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
return re.test(email);
|
return re.test(email);
|
||||||
}
|
}
|
||||||
function draw_wl_policy_domain_table() {
|
function draw_wl_policy_domain_table() {
|
||||||
$('#wl_policy_domain_table').DataTable({
|
$('#wl_policy_domain_table').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
pageLength: pagination_size,
|
||||||
"tr" +
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
"tr" +
|
||||||
language: lang_datatables,
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
ajax: {
|
language: lang_datatables,
|
||||||
type: "GET",
|
ajax: {
|
||||||
url: '/api/v1/get/policy_wl_domain/' + table_for_domain,
|
type: "GET",
|
||||||
dataSrc: function(data){
|
url: '/api/v1/get/policy_wl_domain/' + table_for_domain,
|
||||||
$.each(data, function (i, item) {
|
dataSrc: function(data){
|
||||||
if (!validateEmail(item.object)) {
|
$.each(data, function (i, item) {
|
||||||
item.chkbox = '<input type="checkbox" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />';
|
if (!validateEmail(item.object)) {
|
||||||
}
|
item.chkbox = '<input type="checkbox" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />';
|
||||||
else {
|
}
|
||||||
item.chkbox = '<input type="checkbox" disabled title="' + lang_user.spamfilter_table_domain_policy + '" />';
|
else {
|
||||||
}
|
item.chkbox = '<input type="checkbox" disabled title="' + lang_user.spamfilter_table_domain_policy + '" />';
|
||||||
});
|
}
|
||||||
|
});
|
||||||
return data;
|
|
||||||
}
|
return data;
|
||||||
},
|
}
|
||||||
columns: [
|
},
|
||||||
{
|
columns: [
|
||||||
// placeholder, so checkbox will not block child row toggle
|
{
|
||||||
title: '',
|
// placeholder, so checkbox will not block child row toggle
|
||||||
data: null,
|
title: '',
|
||||||
searchable: false,
|
data: null,
|
||||||
orderable: false,
|
searchable: false,
|
||||||
defaultContent: ''
|
orderable: false,
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: '',
|
{
|
||||||
data: 'chkbox',
|
title: '',
|
||||||
searchable: false,
|
data: 'chkbox',
|
||||||
orderable: false,
|
searchable: false,
|
||||||
defaultContent: ''
|
orderable: false,
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: 'ID',
|
{
|
||||||
data: 'prefid',
|
title: 'ID',
|
||||||
defaultContent: ''
|
data: 'prefid',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang_user.spamfilter_table_rule,
|
{
|
||||||
data: 'value',
|
title: lang_user.spamfilter_table_rule,
|
||||||
defaultContent: ''
|
data: 'value',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: 'Scope',
|
{
|
||||||
data: 'object',
|
title: 'Scope',
|
||||||
defaultContent: ''
|
data: 'object',
|
||||||
}
|
defaultContent: ''
|
||||||
]
|
}
|
||||||
});
|
]
|
||||||
}
|
});
|
||||||
function draw_bl_policy_domain_table() {
|
}
|
||||||
$('#bl_policy_domain_table').DataTable({
|
function draw_bl_policy_domain_table() {
|
||||||
responsive: true,
|
$('#bl_policy_domain_table').DataTable({
|
||||||
processing: true,
|
responsive: true,
|
||||||
serverSide: false,
|
processing: true,
|
||||||
stateSave: true,
|
serverSide: false,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
stateSave: true,
|
||||||
"tr" +
|
pageLength: pagination_size,
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
language: lang_datatables,
|
"tr" +
|
||||||
ajax: {
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
type: "GET",
|
language: lang_datatables,
|
||||||
url: '/api/v1/get/policy_bl_domain/' + table_for_domain,
|
ajax: {
|
||||||
dataSrc: function(data){
|
type: "GET",
|
||||||
$.each(data, function (i, item) {
|
url: '/api/v1/get/policy_bl_domain/' + table_for_domain,
|
||||||
if (!validateEmail(item.object)) {
|
dataSrc: function(data){
|
||||||
item.chkbox = '<input type="checkbox" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />';
|
$.each(data, function (i, item) {
|
||||||
}
|
if (!validateEmail(item.object)) {
|
||||||
else {
|
item.chkbox = '<input type="checkbox" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />';
|
||||||
item.chkbox = '<input type="checkbox" disabled tooltip="' + lang_user.spamfilter_table_domain_policy + '" />';
|
}
|
||||||
}
|
else {
|
||||||
});
|
item.chkbox = '<input type="checkbox" disabled tooltip="' + lang_user.spamfilter_table_domain_policy + '" />';
|
||||||
|
}
|
||||||
return data;
|
});
|
||||||
}
|
|
||||||
},
|
return data;
|
||||||
columns: [
|
}
|
||||||
{
|
},
|
||||||
// placeholder, so checkbox will not block child row toggle
|
columns: [
|
||||||
title: '',
|
{
|
||||||
data: null,
|
// placeholder, so checkbox will not block child row toggle
|
||||||
searchable: false,
|
title: '',
|
||||||
orderable: false,
|
data: null,
|
||||||
defaultContent: ''
|
searchable: false,
|
||||||
},
|
orderable: false,
|
||||||
{
|
defaultContent: ''
|
||||||
title: '',
|
},
|
||||||
data: 'chkbox',
|
{
|
||||||
searchable: false,
|
title: '',
|
||||||
orderable: false,
|
data: 'chkbox',
|
||||||
defaultContent: ''
|
searchable: false,
|
||||||
},
|
orderable: false,
|
||||||
{
|
defaultContent: ''
|
||||||
title: 'ID',
|
},
|
||||||
data: 'prefid',
|
{
|
||||||
defaultContent: ''
|
title: 'ID',
|
||||||
},
|
data: 'prefid',
|
||||||
{
|
defaultContent: ''
|
||||||
title: lang_user.spamfilter_table_rule,
|
},
|
||||||
data: 'value',
|
{
|
||||||
defaultContent: ''
|
title: lang_user.spamfilter_table_rule,
|
||||||
},
|
data: 'value',
|
||||||
{
|
defaultContent: ''
|
||||||
title: 'Scope',
|
},
|
||||||
data: 'object',
|
{
|
||||||
defaultContent: ''
|
title: 'Scope',
|
||||||
}
|
data: 'object',
|
||||||
]
|
defaultContent: ''
|
||||||
});
|
}
|
||||||
}
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
// detect element visibility changes
|
|
||||||
function onVisible(element, callback) {
|
|
||||||
$(document).ready(function() {
|
// detect element visibility changes
|
||||||
element_object = document.querySelector(element);
|
function onVisible(element, callback) {
|
||||||
if (element_object === null) return;
|
$(document).ready(function() {
|
||||||
|
element_object = document.querySelector(element);
|
||||||
new IntersectionObserver((entries, observer) => {
|
if (element_object === null) return;
|
||||||
entries.forEach(entry => {
|
|
||||||
if(entry.intersectionRatio > 0) {
|
new IntersectionObserver((entries, observer) => {
|
||||||
callback(element_object);
|
entries.forEach(entry => {
|
||||||
observer.disconnect();
|
if(entry.intersectionRatio > 0) {
|
||||||
}
|
callback(element_object);
|
||||||
});
|
observer.disconnect();
|
||||||
}).observe(element_object);
|
}
|
||||||
});
|
});
|
||||||
}
|
}).observe(element_object);
|
||||||
// Draw Table if tab is active
|
});
|
||||||
onVisible("[id^=wl_policy_domain_table]", () => draw_wl_policy_domain_table());
|
}
|
||||||
onVisible("[id^=bl_policy_domain_table]", () => draw_bl_policy_domain_table());
|
// Draw Table if tab is active
|
||||||
});
|
onVisible("[id^=wl_policy_domain_table]", () => draw_wl_policy_domain_table());
|
||||||
|
onVisible("[id^=bl_policy_domain_table]", () => draw_bl_policy_domain_table());
|
||||||
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,71 +1,71 @@
|
||||||
jQuery(function($){
|
jQuery(function($){
|
||||||
var qitem = $('legend').data('hash');
|
var qitem = $('legend').data('hash');
|
||||||
var qError = $("#qid_error");
|
var qError = $("#qid_error");
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/inc/ajax/qitem_details.php',
|
url: '/inc/ajax/qitem_details.php',
|
||||||
data: { hash: qitem },
|
data: { hash: qitem },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(data){
|
success: function(data){
|
||||||
$('[data-id="qitems_single"]').each(function(index) {
|
$('[data-id="qitems_single"]').each(function(index) {
|
||||||
$(this).attr("data-item", qitem);
|
$(this).attr("data-item", qitem);
|
||||||
});
|
});
|
||||||
$('#qid_detail_subj').text(data.subject);
|
$('#qid_detail_subj').text(data.subject);
|
||||||
$('#qid_detail_hfrom').text(data.header_from);
|
$('#qid_detail_hfrom').text(data.header_from);
|
||||||
$('#qid_detail_efrom').text(data.env_from);
|
$('#qid_detail_efrom').text(data.env_from);
|
||||||
$('#qid_detail_score').html('');
|
$('#qid_detail_score').html('');
|
||||||
$('#qid_detail_symbols').html('');
|
$('#qid_detail_symbols').html('');
|
||||||
$('#qid_detail_recipients').html('');
|
$('#qid_detail_recipients').html('');
|
||||||
$('#qid_detail_fuzzy').html('');
|
$('#qid_detail_fuzzy').html('');
|
||||||
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
|
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
|
||||||
$.each(data.fuzzy_hashes, function (index, value) {
|
$.each(data.fuzzy_hashes, function (index, value) {
|
||||||
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
|
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$('#qid_detail_fuzzy').append('-');
|
$('#qid_detail_fuzzy').append('-');
|
||||||
}
|
}
|
||||||
if (typeof data.symbols !== 'undefined') {
|
if (typeof data.symbols !== 'undefined') {
|
||||||
data.symbols.sort(function (a, b) {
|
data.symbols.sort(function (a, b) {
|
||||||
if (a.score === 0) return 1
|
if (a.score === 0) return 1;
|
||||||
if (b.score === 0) return -1
|
if (b.score === 0) return -1;
|
||||||
if (b.score < 0 && a.score < 0) {
|
if (b.score < 0 && a.score < 0) {
|
||||||
return a.score - b.score
|
return a.score - b.score;
|
||||||
}
|
}
|
||||||
if (b.score > 0 && a.score > 0) {
|
if (b.score > 0 && a.score > 0) {
|
||||||
return b.score - a.score
|
return b.score - a.score;
|
||||||
}
|
}
|
||||||
return b.score - a.score
|
return b.score - a.score;
|
||||||
})
|
})
|
||||||
$.each(data.symbols, function (index, value) {
|
$.each(data.symbols, function (index, value) {
|
||||||
var highlightClass = ''
|
var highlightClass = '';
|
||||||
if (value.score > 0) highlightClass = 'negative'
|
if (value.score > 0) highlightClass = 'negative';
|
||||||
else if (value.score < 0) highlightClass = 'positive'
|
else if (value.score < 0) highlightClass = 'positive';
|
||||||
else highlightClass = 'neutral'
|
else highlightClass = 'neutral';
|
||||||
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
|
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
|
||||||
});
|
});
|
||||||
$('[data-bs-toggle="tooltip"]').tooltip()
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
||||||
}
|
}
|
||||||
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
|
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
|
||||||
if (data.action === "add header") {
|
if (data.action === "add header") {
|
||||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
|
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
|
||||||
} else if (data.action === "reject") {
|
} else if (data.action === "reject") {
|
||||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
|
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
|
||||||
} else if (data.action === "rewrite subject") {
|
} else if (data.action === "rewrite subject") {
|
||||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
|
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof data.recipients !== 'undefined') {
|
if (typeof data.recipients !== 'undefined') {
|
||||||
$.each(data.recipients, function(index, value) {
|
$.each(data.recipients, function(index, value) {
|
||||||
var elem = $('<span class="mail-address-item"></span>');
|
var elem = $('<span class="mail-address-item"></span>');
|
||||||
elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
|
elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
|
||||||
$('#qid_detail_recipients').append(elem);
|
$('#qid_detail_recipients').append(elem);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(data){
|
error: function(data){
|
||||||
if (typeof data.error !== 'undefined') {
|
if (typeof data.error !== 'undefined') {
|
||||||
qError.text("Error loading quarantine item");
|
qError.text("Error loading quarantine item");
|
||||||
qError.show();
|
qError.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,286 +1,297 @@
|
||||||
// Base64 functions
|
// Base64 functions
|
||||||
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
|
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
|
||||||
|
|
||||||
jQuery(function($){
|
jQuery(function($){
|
||||||
acl_data = JSON.parse(acl);
|
acl_data = JSON.parse(acl);
|
||||||
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
|
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
|
||||||
var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};
|
var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};
|
||||||
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
|
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
|
||||||
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
|
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
|
||||||
$(".refresh_table").on('click', function(e) {
|
$(".refresh_table").on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var table_name = $(this).data('table');
|
var table_name = $(this).data('table');
|
||||||
$('#' + table_name).DataTable().ajax.reload();
|
$('#' + table_name).DataTable().ajax.reload();
|
||||||
});
|
});
|
||||||
function draw_quarantine_table() {
|
function draw_quarantine_table() {
|
||||||
var table = $('#quarantinetable').DataTable({
|
var table = $('#quarantinetable').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
serverSide: false,
|
serverSide: false,
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
pageLength: pagination_size,
|
||||||
"tr" +
|
order: [[2, 'desc']],
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
lengthMenu: [
|
||||||
language: lang_datatables,
|
[10, 25, 50, 100, -1],
|
||||||
initComplete: function(){
|
[10, 25, 50, 100, 'all']
|
||||||
hideTableExpandCollapseBtn('#quarantinetable');
|
],
|
||||||
},
|
pagingType: 'first_last_numbers',
|
||||||
ajax: {
|
aColumns: [
|
||||||
type: "GET",
|
{ sWidth: '8.25%' },
|
||||||
url: "/api/v1/get/quarantine/all",
|
{ sClass: 'classDataTable' }
|
||||||
dataSrc: function(data){
|
],
|
||||||
$.each(data, function (i, item) {
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
if (item.subject === null) {
|
"tr" +
|
||||||
item.subject = '';
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
} else {
|
language: lang_datatables,
|
||||||
item.subject = escapeHtml(item.subject);
|
initComplete: function(){
|
||||||
}
|
hideTableExpandCollapseBtn('#quarantinetable');
|
||||||
if (item.score === null) {
|
},
|
||||||
item.score = '-';
|
ajax: {
|
||||||
}
|
type: "GET",
|
||||||
if (item.virus_flag > 0) {
|
url: "/api/v1/get/quarantine/all",
|
||||||
item.virus = '<span class="badge fs-6 bg-danger">' + lang.high_danger + '</span>';
|
dataSrc: function(data){
|
||||||
} else {
|
$.each(data, function (i, item) {
|
||||||
item.virus = '<span class="badge fs-6 bg-secondary">' + lang.neutral_danger + '</span>';
|
if (item.subject === null) {
|
||||||
}
|
item.subject = '';
|
||||||
if (item.action === "reject") {
|
} else {
|
||||||
item.rspamdaction = '<span class="badge fs-6 bg-danger">' + lang.rejected + '</span>';
|
item.subject = escapeHtml(item.subject);
|
||||||
} else if (item.action === "add header") {
|
}
|
||||||
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.junk_folder + '</span>';
|
if (item.score === null) {
|
||||||
} else if (item.action === "rewrite subject") {
|
item.score = '-';
|
||||||
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.rewrite_subject + '</span>';
|
}
|
||||||
}
|
if (item.virus_flag > 0) {
|
||||||
if(item.notified > 0) {
|
item.virus = '<span class="badge fs-6 bg-danger">' + lang.high_danger + '</span>';
|
||||||
item.notified = '✔';
|
} else {
|
||||||
} else {
|
item.virus = '<span class="badge fs-6 bg-secondary">' + lang.neutral_danger + '</span>';
|
||||||
item.notified = '✖';
|
}
|
||||||
}
|
if (item.action === "reject") {
|
||||||
if (acl_data.login_as === 1) {
|
item.rspamdaction = '<span class="badge fs-6 bg-danger">' + lang.rejected + '</span>';
|
||||||
item.action = '<div class="btn-group">' +
|
} else if (item.action === "add header") {
|
||||||
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-info show_qid_info"><i class="bi bi-box-arrow-up-right"></i> ' + lang.show_item + '</a>' +
|
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.junk_folder + '</span>';
|
||||||
'<a href="#" data-action="delete_selected" data-id="del-single-qitem" data-api-url="delete/qitem" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
|
} else if (item.action === "rewrite subject") {
|
||||||
'</div>';
|
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.rewrite_subject + '</span>';
|
||||||
}
|
}
|
||||||
else {
|
if(item.notified > 0) {
|
||||||
item.action = '<div class="btn-group">' +
|
item.notified = '✔';
|
||||||
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><i class="bi bi-file-earmark-text"></i> ' + lang.show_item + '</a>' +
|
} else {
|
||||||
'</div>';
|
item.notified = '✖';
|
||||||
}
|
}
|
||||||
item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
|
if (acl_data.login_as === 1) {
|
||||||
});
|
item.action = '<div class="btn-group">' +
|
||||||
|
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-info show_qid_info"><i class="bi bi-box-arrow-up-right"></i> ' + lang.show_item + '</a>' +
|
||||||
return data;
|
'<a href="#" data-action="delete_selected" data-id="del-single-qitem" data-api-url="delete/qitem" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
|
||||||
}
|
'</div>';
|
||||||
},
|
}
|
||||||
columns: [
|
else {
|
||||||
{
|
item.action = '<div class="btn-group">' +
|
||||||
// placeholder, so checkbox will not block child row toggle
|
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><i class="bi bi-file-earmark-text"></i> ' + lang.show_item + '</a>' +
|
||||||
title: '',
|
'</div>';
|
||||||
data: null,
|
}
|
||||||
searchable: false,
|
item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
|
||||||
orderable: false,
|
});
|
||||||
defaultContent: ''
|
|
||||||
},
|
return data;
|
||||||
{
|
}
|
||||||
title: '',
|
},
|
||||||
data: 'chkbox',
|
columns: [
|
||||||
searchable: false,
|
{
|
||||||
orderable: false,
|
// placeholder, so checkbox will not block child row toggle
|
||||||
defaultContent: ''
|
title: '',
|
||||||
},
|
data: null,
|
||||||
{
|
searchable: false,
|
||||||
title: 'ID',
|
orderable: false,
|
||||||
data: 'id',
|
defaultContent: ''
|
||||||
defaultContent: ''
|
},
|
||||||
},
|
{
|
||||||
{
|
title: '',
|
||||||
title: lang.qid,
|
data: 'chkbox',
|
||||||
data: 'qid',
|
searchable: false,
|
||||||
defaultContent: ''
|
orderable: false,
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.sender,
|
{
|
||||||
data: 'sender',
|
title: 'ID',
|
||||||
defaultContent: ''
|
data: 'id',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.subj,
|
{
|
||||||
data: 'subject',
|
title: lang.qid,
|
||||||
defaultContent: ''
|
data: 'qid',
|
||||||
},
|
defaultContent: ''
|
||||||
{
|
},
|
||||||
title: lang.rspamd_result,
|
{
|
||||||
data: 'rspamdaction',
|
title: lang.sender,
|
||||||
defaultContent: ''
|
data: 'sender',
|
||||||
},
|
className: 'senders-mw220',
|
||||||
{
|
defaultContent: ''
|
||||||
title: lang.rcpt,
|
},
|
||||||
data: 'rcpt',
|
{
|
||||||
defaultContent: ''
|
title: lang.subj,
|
||||||
},
|
data: 'subject',
|
||||||
{
|
defaultContent: ''
|
||||||
title: lang.danger,
|
},
|
||||||
data: 'virus',
|
{
|
||||||
defaultContent: ''
|
title: lang.rspamd_result,
|
||||||
},
|
data: 'rspamdaction',
|
||||||
{
|
defaultContent: ''
|
||||||
title: lang.spam_score,
|
},
|
||||||
data: 'score',
|
{
|
||||||
defaultContent: ''
|
title: lang.rcpt,
|
||||||
},
|
data: 'rcpt',
|
||||||
{
|
defaultContent: ''
|
||||||
title: lang.notified,
|
},
|
||||||
data: 'notified',
|
{
|
||||||
defaultContent: ''
|
title: lang.danger,
|
||||||
},
|
data: 'virus',
|
||||||
{
|
defaultContent: ''
|
||||||
title: lang.received,
|
},
|
||||||
data: 'created',
|
{
|
||||||
defaultContent: '',
|
title: lang.spam_score,
|
||||||
createdCell: function(td, cellData) {
|
data: 'score',
|
||||||
$(td).attr({
|
defaultContent: ''
|
||||||
"data-order": cellData,
|
},
|
||||||
"data-sort": cellData
|
{
|
||||||
});
|
title: lang.notified,
|
||||||
|
data: 'notified',
|
||||||
var date = new Date(cellData ? cellData * 1000 : 0);
|
defaultContent: ''
|
||||||
var dateString = date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
},
|
||||||
$(td).html(dateString);
|
{
|
||||||
}
|
title: lang.received,
|
||||||
},
|
data: 'created',
|
||||||
{
|
defaultContent: '',
|
||||||
title: lang.action,
|
createdCell: function(td, cellData) {
|
||||||
data: 'action',
|
$(td).attr({
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
"data-order": cellData,
|
||||||
defaultContent: ''
|
"data-sort": cellData
|
||||||
},
|
});
|
||||||
]
|
|
||||||
});
|
var date = new Date(cellData ? cellData * 1000 : 0);
|
||||||
|
var dateString = date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
||||||
table.on('responsive-resize', function (e, datatable, columns){
|
$(td).html(dateString);
|
||||||
hideTableExpandCollapseBtn('#quarantinetable');
|
}
|
||||||
});
|
},
|
||||||
}
|
{
|
||||||
|
title: lang.action,
|
||||||
$('body').on('click', '.show_qid_info', function (e) {
|
data: 'action',
|
||||||
e.preventDefault();
|
className: 'dt-text-right dt-sm-head-hidden',
|
||||||
var qitem = $(this).attr('data-item');
|
defaultContent: ''
|
||||||
var qError = $("#qid_error");
|
},
|
||||||
|
]
|
||||||
$('#qidDetailModal').modal('show');
|
});
|
||||||
qError.hide();
|
|
||||||
|
table.on('responsive-resize', function (e, datatable, columns){
|
||||||
$.ajax({
|
hideTableExpandCollapseBtn('#quarantinetable');
|
||||||
url: '/inc/ajax/qitem_details.php',
|
});
|
||||||
data: { id: qitem },
|
}
|
||||||
dataType: 'json',
|
|
||||||
success: function(data){
|
$('body').on('click', '.show_qid_info', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
$('[data-id="qitems_single"]').each(function(index) {
|
var qitem = $(this).attr('data-item');
|
||||||
$(this).attr("data-item", qitem);
|
var qError = $("#qid_error");
|
||||||
});
|
|
||||||
|
$('#qidDetailModal').modal('show');
|
||||||
$("#quick_download_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&eml', '_blank')");
|
qError.hide();
|
||||||
$("#quick_release_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_release', '_blank')");
|
|
||||||
$("#quick_delete_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_delete', '_blank')");
|
$.ajax({
|
||||||
|
url: '/inc/ajax/qitem_details.php',
|
||||||
$('#qid_detail_subj').text(data.subject);
|
data: { id: qitem },
|
||||||
$('#qid_detail_hfrom').text(data.header_from);
|
dataType: 'json',
|
||||||
$('#qid_detail_efrom').text(data.env_from);
|
success: function(data){
|
||||||
$('#qid_detail_score').html('');
|
|
||||||
$('#qid_detail_recipients').html('');
|
$('[data-id="qitems_single"]').each(function(index) {
|
||||||
$('#qid_detail_symbols').html('');
|
$(this).attr("data-item", qitem);
|
||||||
$('#qid_detail_fuzzy').html('');
|
});
|
||||||
if (typeof data.symbols !== 'undefined') {
|
|
||||||
data.symbols.sort(function (a, b) {
|
$("#quick_download_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&eml', '_blank')");
|
||||||
if (a.score === 0) return 1
|
$("#quick_release_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_release', '_blank')");
|
||||||
if (b.score === 0) return -1
|
$("#quick_delete_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_delete', '_blank')");
|
||||||
if (b.score < 0 && a.score < 0) {
|
|
||||||
return a.score - b.score
|
$('#qid_detail_subj').text(data.subject);
|
||||||
}
|
$('#qid_detail_hfrom').text(data.header_from);
|
||||||
if (b.score > 0 && a.score > 0) {
|
$('#qid_detail_efrom').text(data.env_from);
|
||||||
return b.score - a.score
|
$('#qid_detail_score').html('');
|
||||||
}
|
$('#qid_detail_recipients').html('');
|
||||||
return b.score - a.score
|
$('#qid_detail_symbols').html('');
|
||||||
})
|
$('#qid_detail_fuzzy').html('');
|
||||||
$.each(data.symbols, function (index, value) {
|
if (typeof data.symbols !== 'undefined') {
|
||||||
var highlightClass = ''
|
data.symbols.sort(function (a, b) {
|
||||||
if (value.score > 0) highlightClass = 'negative'
|
if (a.score === 0) return 1;
|
||||||
else if (value.score < 0) highlightClass = 'positive'
|
if (b.score === 0) return -1;
|
||||||
else highlightClass = 'neutral'
|
if (b.score < 0 && a.score < 0) {
|
||||||
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
|
return a.score - b.score;
|
||||||
});
|
}
|
||||||
$('[data-bs-toggle="tooltip"]').tooltip()
|
if (b.score > 0 && a.score > 0) {
|
||||||
}
|
return b.score - a.score;
|
||||||
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
|
}
|
||||||
$.each(data.fuzzy_hashes, function (index, value) {
|
return b.score - a.score;
|
||||||
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
|
})
|
||||||
});
|
$.each(data.symbols, function (index, value) {
|
||||||
} else {
|
var highlightClass = '';
|
||||||
$('#qid_detail_fuzzy').append('-');
|
if (value.score > 0) highlightClass = 'negative';
|
||||||
}
|
else if (value.score < 0) highlightClass = 'positive';
|
||||||
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
|
else highlightClass = 'neutral';
|
||||||
if (data.action == "add header") {
|
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
|
||||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
|
});
|
||||||
} else if (data.action == "reject") {
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
||||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
|
}
|
||||||
} else if (data.action == "rewrite subject") {
|
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
|
||||||
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
|
$.each(data.fuzzy_hashes, function (index, value) {
|
||||||
}
|
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
|
||||||
}
|
});
|
||||||
if (typeof data.recipients !== 'undefined') {
|
} else {
|
||||||
$.each(data.recipients, function(index, value) {
|
$('#qid_detail_fuzzy').append('-');
|
||||||
var elem = $('<span class="mail-address-item"></span>');
|
}
|
||||||
elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
|
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
|
||||||
$('#qid_detail_recipients').append(elem);
|
if (data.action == "add header") {
|
||||||
});
|
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
|
||||||
}
|
} else if (data.action == "reject") {
|
||||||
$('#qid_detail_text').text(data.text_plain);
|
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
|
||||||
$('#qid_detail_text_from_html').text(data.text_html);
|
} else if (data.action == "rewrite subject") {
|
||||||
var qAtts = $("#qid_detail_atts");
|
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
|
||||||
if (typeof data.attachments !== 'undefined') {
|
}
|
||||||
qAtts.text('');
|
}
|
||||||
$.each(data.attachments, function(index, value) {
|
if (typeof data.recipients !== 'undefined') {
|
||||||
qAtts.append(
|
$.each(data.recipients, function(index, value) {
|
||||||
'<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' +
|
var elem = $('<span class="mail-address-item"></span>');
|
||||||
' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>'
|
elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
|
||||||
);
|
$('#qid_detail_recipients').append(elem);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
$('#qid_detail_text').text(data.text_plain);
|
||||||
qAtts.text('-');
|
$('#qid_detail_text_from_html').text(data.text_html);
|
||||||
}
|
var qAtts = $("#qid_detail_atts");
|
||||||
},
|
if (typeof data.attachments !== 'undefined') {
|
||||||
error: function(data){
|
qAtts.text('');
|
||||||
if (typeof data.error !== 'undefined') {
|
$.each(data.attachments, function(index, value) {
|
||||||
$('#qid_detail_subj').text('-');
|
qAtts.append(
|
||||||
$('#qid_detail_hfrom').text('-');
|
'<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' +
|
||||||
$('#qid_detail_efrom').text('-');
|
' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>'
|
||||||
$('#qid_detail_score').html('-');
|
);
|
||||||
$('#qid_detail_recipients').html('-');
|
});
|
||||||
$('#qid_detail_symbols').html('-');
|
}
|
||||||
$('#qid_detail_fuzzy').html('-');
|
else {
|
||||||
$('#qid_detail_text').text('-');
|
qAtts.text('-');
|
||||||
$('#qid_detail_text_from_html').text('-');
|
}
|
||||||
qError.text("Error loading quarantine item");
|
},
|
||||||
qError.show();
|
error: function(data){
|
||||||
}
|
if (typeof data.error !== 'undefined') {
|
||||||
}
|
$('#qid_detail_subj').text('-');
|
||||||
});
|
$('#qid_detail_hfrom').text('-');
|
||||||
});
|
$('#qid_detail_efrom').text('-');
|
||||||
|
$('#qid_detail_score').html('-');
|
||||||
$('body').on('click', 'span.footable-toggle', function () {
|
$('#qid_detail_recipients').html('-');
|
||||||
event.stopPropagation();
|
$('#qid_detail_symbols').html('-');
|
||||||
})
|
$('#qid_detail_fuzzy').html('-');
|
||||||
|
$('#qid_detail_text').text('-');
|
||||||
// Initial table drawings
|
$('#qid_detail_text_from_html').text('-');
|
||||||
draw_quarantine_table();
|
qError.text("Error loading quarantine item");
|
||||||
|
qError.show();
|
||||||
|
}
|
||||||
function hideTableExpandCollapseBtn(table){
|
}
|
||||||
if ($(table).hasClass('collapsed'))
|
});
|
||||||
$(".table_collapse_option").show();
|
});
|
||||||
else
|
|
||||||
$(".table_collapse_option").hide();
|
$('body').on('click', 'span.footable-toggle', function () {
|
||||||
}
|
event.stopPropagation();
|
||||||
});
|
})
|
||||||
|
|
||||||
|
// Initial table drawings
|
||||||
|
draw_quarantine_table();
|
||||||
|
|
||||||
|
function hideTableExpandCollapseBtn(table){
|
||||||
|
if ($(table).hasClass('collapsed'))
|
||||||
|
$(".table_collapse_option").show();
|
||||||
|
else
|
||||||
|
$(".table_collapse_option").hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -1,128 +1,128 @@
|
||||||
jQuery(function($){
|
jQuery(function($){
|
||||||
|
|
||||||
$(".refresh_table").on('click', function(e) {
|
$(".refresh_table").on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var table_name = $(this).data('table');
|
var table_name = $(this).data('table');
|
||||||
$('#' + table_name).DataTable().ajax.reload();
|
$('#' + table_name).DataTable().ajax.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
|
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
|
||||||
|
|
||||||
// Queue item
|
// Queue item
|
||||||
$('#showQueuedMsg').on('show.bs.modal', function (e) {
|
$('#showQueuedMsg').on('show.bs.modal', function (e) {
|
||||||
$('#queue_msg_content').text(lang.loading);
|
$('#queue_msg_content').text(lang.loading);
|
||||||
button = $(e.relatedTarget)
|
button = $(e.relatedTarget)
|
||||||
if (button != null) {
|
if (button != null) {
|
||||||
$('#queue_id').text(button.data('queue-id'));
|
$('#queue_id').text(button.data('queue-id'));
|
||||||
}
|
|
||||||
$.ajax({
|
|
||||||
type: 'GET',
|
|
||||||
url: '/api/v1/get/postcat/' + button.data('queue-id'),
|
|
||||||
dataType: 'text',
|
|
||||||
complete: function (data) {
|
|
||||||
console.log(data);
|
|
||||||
$('#queue_msg_content').text(data.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
function draw_queue() {
|
|
||||||
// just recalc width if instance already exists
|
|
||||||
if ($.fn.DataTable.isDataTable('#queuetable') ) {
|
|
||||||
$('#queuetable').DataTable().columns.adjust().responsive.recalc();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: '/api/v1/get/postcat/' + button.data('queue-id'),
|
||||||
|
dataType: 'text',
|
||||||
|
complete: function (data) {
|
||||||
|
$('#queue_msg_content').text(data.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
$('#queuetable').DataTable({
|
function draw_queue() {
|
||||||
responsive: true,
|
// just recalc width if instance already exists
|
||||||
processing: true,
|
if ($.fn.DataTable.isDataTable('#queuetable') ) {
|
||||||
serverSide: false,
|
$('#queuetable').DataTable().columns.adjust().responsive.recalc();
|
||||||
stateSave: true,
|
return;
|
||||||
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
}
|
||||||
"tr" +
|
|
||||||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
$('#queuetable').DataTable({
|
||||||
language: lang_datatables,
|
responsive: true,
|
||||||
ajax: {
|
processing: true,
|
||||||
type: "GET",
|
serverSide: false,
|
||||||
url: "/api/v1/get/mailq/all",
|
stateSave: true,
|
||||||
dataSrc: function(data){
|
pageLength: pagination_size,
|
||||||
$.each(data, function (i, item) {
|
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
|
||||||
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
|
"tr" +
|
||||||
rcpts = $.map(item.recipients, function(i) {
|
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||||||
return escapeHtml(i);
|
language: lang_datatables,
|
||||||
});
|
ajax: {
|
||||||
item.recipients = rcpts.join('<hr style="margin:1px!important">');
|
type: "GET",
|
||||||
item.action = '<div class="btn-group">' +
|
url: "/api/v1/get/mailq/all",
|
||||||
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.queue_show_message + '</a>' +
|
dataSrc: function(data){
|
||||||
|
$.each(data, function (i, item) {
|
||||||
|
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
|
||||||
|
rcpts = $.map(item.recipients, function(i) {
|
||||||
|
return escapeHtml(i);
|
||||||
|
});
|
||||||
|
item.recipients = rcpts.join('<hr style="margin:1px!important">');
|
||||||
|
item.action = '<div class="btn-group">' +
|
||||||
|
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
// placeholder, so checkbox will not block child row toggle
|
// placeholder, so checkbox will not block child row toggle
|
||||||
title: '',
|
title: '',
|
||||||
data: null,
|
data: null,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
data: 'chkbox',
|
data: 'chkbox',
|
||||||
searchable: false,
|
searchable: false,
|
||||||
orderable: false,
|
orderable: false,
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'QID',
|
title: 'QID',
|
||||||
data: 'queue_id',
|
data: 'queue_id',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Queue',
|
title: 'Queue',
|
||||||
data: 'queue_name',
|
data: 'queue_name',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.arrival_time,
|
title: lang_admin.arrival_time,
|
||||||
data: 'arrival_time',
|
data: 'arrival_time',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type){
|
render: function (data, type){
|
||||||
var date = new Date(data ? data * 1000 : 0);
|
var date = new Date(data ? data * 1000 : 0);
|
||||||
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.message_size,
|
title: lang_admin.message_size,
|
||||||
data: 'message_size',
|
data: 'message_size',
|
||||||
defaultContent: '',
|
defaultContent: '',
|
||||||
render: function (data, type){
|
render: function (data, type){
|
||||||
return humanFileSize(data);
|
return humanFileSize(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.sender,
|
title: lang_admin.sender,
|
||||||
data: 'sender',
|
data: 'sender',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.recipients,
|
title: lang_admin.recipients,
|
||||||
data: 'recipients',
|
data: 'recipients',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: lang_admin.action,
|
title: lang_admin.action,
|
||||||
data: 'action',
|
data: 'action',
|
||||||
className: 'text-md-end dt-sm-head-hidden dt-body-right',
|
className: 'dt-sm-head-hidden dt-text-right',
|
||||||
defaultContent: ''
|
defaultContent: ''
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_queue();
|
draw_queue();
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -288,6 +288,18 @@ if (isset($_GET['query'])) {
|
||||||
case "domain-admin":
|
case "domain-admin":
|
||||||
process_add_return(domain_admin('add', $attr));
|
process_add_return(domain_admin('add', $attr));
|
||||||
break;
|
break;
|
||||||
|
case "sso":
|
||||||
|
switch ($object) {
|
||||||
|
case "domain-admin":
|
||||||
|
$data = domain_admin_sso('issue', $attr);
|
||||||
|
if($data) {
|
||||||
|
echo json_encode($data);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
process_add_return($data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "admin":
|
case "admin":
|
||||||
process_add_return(admin('add', $attr));
|
process_add_return(admin('add', $attr));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -650,7 +650,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Přihlášení zpožděno o %s sekund.",
|
"delayed": "Přihlášení zpožděno o %s sekund.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Přihlásit",
|
"login": "Přihlásit",
|
||||||
"mobileconfig_info": "Ke stažení profilového souboru se přihlaste jako uživatel schránky.",
|
"mobileconfig_info": "Ke stažení profilového souboru se přihlaste jako uživatel schránky.",
|
||||||
"other_logins": "Přihlášení klíčem",
|
"other_logins": "Přihlášení klíčem",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"acl": {
|
"acl": {
|
||||||
"alias_domains": "Tilføj kældenavn domæner",
|
"alias_domains": "Tilføj domænealias",
|
||||||
"app_passwds": "Administrer app-adgangskoder",
|
"app_passwds": "Administrer app-adgangskoder",
|
||||||
"bcc_maps": "BCC kort",
|
"bcc_maps": "BCC kort",
|
||||||
"delimiter_action": "Afgrænsning handling",
|
"delimiter_action": "Afgrænsning handling",
|
||||||
|
@ -22,9 +22,9 @@
|
||||||
"spam_alias": "Midlertidige aliasser",
|
"spam_alias": "Midlertidige aliasser",
|
||||||
"spam_policy": "Sortliste / hvidliste",
|
"spam_policy": "Sortliste / hvidliste",
|
||||||
"spam_score": "Spam-score",
|
"spam_score": "Spam-score",
|
||||||
"syncjobs": "Synkroniser job",
|
"syncjobs": "Synkroniserings job",
|
||||||
"tls_policy": "TLS politik",
|
"tls_policy": "TLS politik",
|
||||||
"unlimited_quota": "Ubegrænset quote for mailbokse",
|
"unlimited_quota": "Ubegrænset plads for mailbokse",
|
||||||
"domain_desc": "Skift domæne beskrivelse"
|
"domain_desc": "Skift domæne beskrivelse"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
"add": "Tilføj",
|
"add": "Tilføj",
|
||||||
"add_domain_only": "Tilføj kun domæne",
|
"add_domain_only": "Tilføj kun domæne",
|
||||||
"add_domain_restart": "Tilføj domæne og genstart SOGo",
|
"add_domain_restart": "Tilføj domæne og genstart SOGo",
|
||||||
"alias_address": "Alias adresse (r)",
|
"alias_address": "Alias adresse(r)",
|
||||||
"alias_address_info": "<small>Fuld e-mail-adresse eller @ eksempel.com for at fange alle beskeder til et domæne (kommasepareret). <b> kun mailcow-domæner</b>.</small>",
|
"alias_address_info": "<small>Fuld e-mail-adresse eller @ eksempel.com for at fange alle beskeder til et domæne (kommasepareret). <b> kun mailcow-domæner</b>.</small>",
|
||||||
"alias_domain": "Alias-domæne",
|
"alias_domain": "Alias-domæne",
|
||||||
"alias_domain_info": "<small>Kun gyldige domænenavne (kommasepareret).</small>",
|
"alias_domain_info": "<small>Kun gyldige domænenavne (kommasepareret).</small>",
|
||||||
|
@ -586,7 +586,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Login blev forsinket med% s sekunder.",
|
"delayed": "Login blev forsinket med% s sekunder.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"mobileconfig_info": "Log ind som postkassebruger for at downloade den anmodede Apple-forbindelsesprofil.",
|
"mobileconfig_info": "Log ind som postkassebruger for at downloade den anmodede Apple-forbindelsesprofil.",
|
||||||
"other_logins": "Nøgle login",
|
"other_logins": "Nøgle login",
|
||||||
|
|
|
@ -339,7 +339,8 @@
|
||||||
"oauth2_add_client": "Füge OAuth2 Client hinzu",
|
"oauth2_add_client": "Füge OAuth2 Client hinzu",
|
||||||
"api_read_only": "Schreibgeschützter Zugriff",
|
"api_read_only": "Schreibgeschützter Zugriff",
|
||||||
"api_read_write": "Lese-Schreib-Zugriff",
|
"api_read_write": "Lese-Schreib-Zugriff",
|
||||||
"oauth2_apps": "OAuth2 Apps"
|
"oauth2_apps": "OAuth2 Apps",
|
||||||
|
"queue_unban": "entsperren"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
|
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
|
||||||
|
@ -366,7 +367,7 @@
|
||||||
"domain_not_empty": "Domain %s ist nicht leer",
|
"domain_not_empty": "Domain %s ist nicht leer",
|
||||||
"domain_not_found": "Domain %s nicht gefunden",
|
"domain_not_found": "Domain %s nicht gefunden",
|
||||||
"domain_quota_m_in_use": "Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein",
|
"domain_quota_m_in_use": "Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein",
|
||||||
"extended_sender_acl_denied": "Keine Rechte zum setzen von externen Absenderadressen",
|
"extended_sender_acl_denied": "Keine Rechte zum Setzen von externen Absenderadressen",
|
||||||
"extra_acl_invalid": "Externe Absenderadresse \"%s\" ist ungültig",
|
"extra_acl_invalid": "Externe Absenderadresse \"%s\" ist ungültig",
|
||||||
"extra_acl_invalid_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain",
|
"extra_acl_invalid_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain",
|
||||||
"fido2_verification_failed": "FIDO2-Verifizierung fehlgeschlagen: %s",
|
"fido2_verification_failed": "FIDO2-Verifizierung fehlgeschlagen: %s",
|
||||||
|
@ -454,17 +455,23 @@
|
||||||
"totp_verification_failed": "TOTP-Verifizierung fehlgeschlagen",
|
"totp_verification_failed": "TOTP-Verifizierung fehlgeschlagen",
|
||||||
"transport_dest_exists": "Transport-Maps-Ziel \"%s\" existiert bereits",
|
"transport_dest_exists": "Transport-Maps-Ziel \"%s\" existiert bereits",
|
||||||
"webauthn_verification_failed": "WebAuthn-Verifizierung fehlgeschlagen: %s",
|
"webauthn_verification_failed": "WebAuthn-Verifizierung fehlgeschlagen: %s",
|
||||||
|
"webauthn_authenticator_failed": "Der ausgewählte Authenticator wurde nicht gefunden",
|
||||||
|
"webauthn_publickey_failed": "Zu dem ausgewählten Authenticator wurde kein Publickey hinterlegt",
|
||||||
|
"webauthn_username_failed": "Der ausgewählte Authenticator gehört zu einem anderen Konto",
|
||||||
"unknown": "Ein unbekannter Fehler trat auf",
|
"unknown": "Ein unbekannter Fehler trat auf",
|
||||||
"unknown_tfa_method": "Unbekannte TFA-Methode",
|
"unknown_tfa_method": "Unbekannte TFA-Methode",
|
||||||
"unlimited_quota_acl": "Unendliche Quota untersagt durch ACL",
|
"unlimited_quota_acl": "Unendliche Quota untersagt durch ACL",
|
||||||
"username_invalid": "Benutzername %s kann nicht verwendet werden",
|
"username_invalid": "Benutzername %s kann nicht verwendet werden",
|
||||||
"validity_missing": "Bitte geben Sie eine Gültigkeitsdauer an",
|
"validity_missing": "Bitte geben Sie eine Gültigkeitsdauer an",
|
||||||
"value_missing": "Bitte alle Felder ausfüllen",
|
"value_missing": "Bitte alle Felder ausfüllen",
|
||||||
"yotp_verification_failed": "Yubico OTP-Verifizierung fehlgeschlagen: %s"
|
"yotp_verification_failed": "Yubico OTP-Verifizierung fehlgeschlagen: %s",
|
||||||
|
"template_exists": "Vorlage %s existiert bereits",
|
||||||
|
"template_id_invalid": "Vorlagen-ID %s ungültig",
|
||||||
|
"template_name_invalid": "Name der Vorlage ungültig"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
"collapse_all": "Alle Einklappen",
|
"collapse_all": "Alle Einklappen",
|
||||||
"decimal": "",
|
"decimal": ",",
|
||||||
"emptyTable": "Keine Daten in der Tabelle vorhanden",
|
"emptyTable": "Keine Daten in der Tabelle vorhanden",
|
||||||
"expand_all": "Alle Ausklappen",
|
"expand_all": "Alle Ausklappen",
|
||||||
"info": "_START_ bis _END_ von _TOTAL_ Einträgen",
|
"info": "_START_ bis _END_ von _TOTAL_ Einträgen",
|
||||||
|
@ -498,7 +505,7 @@
|
||||||
"current_time": "Systemzeit",
|
"current_time": "Systemzeit",
|
||||||
"disk_usage": "Festplattennutzung",
|
"disk_usage": "Festplattennutzung",
|
||||||
"docs": "Dokumente",
|
"docs": "Dokumente",
|
||||||
"error_show_ip": "konnte die öffentlichen IP Adressen nicht auflösen",
|
"error_show_ip": "Konnte die öffentlichen IP Adressen nicht auflösen",
|
||||||
"external_logs": "Externe Logs",
|
"external_logs": "Externe Logs",
|
||||||
"history_all_servers": "History (alle Server)",
|
"history_all_servers": "History (alle Server)",
|
||||||
"in_memory_logs": "In-memory Logs",
|
"in_memory_logs": "In-memory Logs",
|
||||||
|
@ -651,7 +658,8 @@
|
||||||
"title": "Objekt bearbeiten",
|
"title": "Objekt bearbeiten",
|
||||||
"unchanged_if_empty": "Unverändert, wenn leer",
|
"unchanged_if_empty": "Unverändert, wenn leer",
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
"validate_save": "Validieren und speichern"
|
"validate_save": "Validieren und speichern",
|
||||||
|
"pushover_sound": "Ton"
|
||||||
},
|
},
|
||||||
"fido2": {
|
"fido2": {
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
|
@ -692,7 +700,8 @@
|
||||||
"quarantine": "Quarantäne",
|
"quarantine": "Quarantäne",
|
||||||
"restart_netfilter": "Netfilter neustarten",
|
"restart_netfilter": "Netfilter neustarten",
|
||||||
"restart_sogo": "SOGo neustarten",
|
"restart_sogo": "SOGo neustarten",
|
||||||
"user_settings": "Benutzereinstellungen"
|
"user_settings": "Benutzereinstellungen",
|
||||||
|
"mailcow_system": "System"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"awaiting_tfa_confirmation": "Warte auf TFA-Verifizierung",
|
"awaiting_tfa_confirmation": "Warte auf TFA-Verifizierung",
|
||||||
|
@ -701,7 +710,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Login wurde zur Sicherheit um %s Sekunde/n verzögert.",
|
"delayed": "Login wurde zur Sicherheit um %s Sekunde/n verzögert.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
"mobileconfig_info": "Bitte als Mailbox-Benutzer einloggen, um das Verbindungsprofil herunterzuladen.",
|
"mobileconfig_info": "Bitte als Mailbox-Benutzer einloggen, um das Verbindungsprofil herunterzuladen.",
|
||||||
"other_logins": "Key Login",
|
"other_logins": "Key Login",
|
||||||
|
@ -1236,7 +1245,8 @@
|
||||||
"syncjob_EXIT_CONNECTION_FAILURE": "Verbindungsproblem",
|
"syncjob_EXIT_CONNECTION_FAILURE": "Verbindungsproblem",
|
||||||
"syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung",
|
"syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung",
|
||||||
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
|
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
|
||||||
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Falscher Benutzername oder Passwort"
|
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Falscher Benutzername oder Passwort",
|
||||||
|
"pushover_sound": "Ton"
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"cannot_delete_self": "Kann derzeit eingeloggten Benutzer nicht entfernen",
|
"cannot_delete_self": "Kann derzeit eingeloggten Benutzer nicht entfernen",
|
||||||
|
|
|
@ -458,6 +458,9 @@
|
||||||
"totp_verification_failed": "TOTP verification failed",
|
"totp_verification_failed": "TOTP verification failed",
|
||||||
"transport_dest_exists": "Transport destination \"%s\" exists",
|
"transport_dest_exists": "Transport destination \"%s\" exists",
|
||||||
"webauthn_verification_failed": "WebAuthn verification failed: %s",
|
"webauthn_verification_failed": "WebAuthn verification failed: %s",
|
||||||
|
"webauthn_authenticator_failed": "The selected authenticator was not found",
|
||||||
|
"webauthn_publickey_failed": "No public key was stored for the selected authenticator",
|
||||||
|
"webauthn_username_failed": "The selected authenticator belongs to another account",
|
||||||
"unknown": "An unknown error occurred",
|
"unknown": "An unknown error occurred",
|
||||||
"unknown_tfa_method": "Unknown TFA method",
|
"unknown_tfa_method": "Unknown TFA method",
|
||||||
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
|
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
|
||||||
|
@ -468,7 +471,7 @@
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
"collapse_all": "Collapse All",
|
"collapse_all": "Collapse All",
|
||||||
"decimal": "",
|
"decimal": ".",
|
||||||
"emptyTable": "No data available in table",
|
"emptyTable": "No data available in table",
|
||||||
"expand_all": "Expand All",
|
"expand_all": "Expand All",
|
||||||
"info": "Showing _START_ to _END_ of _TOTAL_ entries",
|
"info": "Showing _START_ to _END_ of _TOTAL_ entries",
|
||||||
|
@ -707,7 +710,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Login was delayed by %s seconds.",
|
"delayed": "Login was delayed by %s seconds.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
|
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
|
||||||
"other_logins": "Key login",
|
"other_logins": "Key login",
|
||||||
|
|
|
@ -612,7 +612,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "La connexion a été retardée de %s secondes.",
|
"delayed": "La connexion a été retardée de %s secondes.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Connexion",
|
"login": "Connexion",
|
||||||
"mobileconfig_info": "Veuillez vous connecter en tant qu’utilisateur de la boîte pour télécharger le profil de connexion Apple demandé.",
|
"mobileconfig_info": "Veuillez vous connecter en tant qu’utilisateur de la boîte pour télécharger le profil de connexion Apple demandé.",
|
||||||
"other_logins": "Clé d'authentification",
|
"other_logins": "Clé d'authentification",
|
||||||
|
|
|
@ -674,7 +674,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "L'accesso è stato ritardato di %s secondi.",
|
"delayed": "L'accesso è stato ritardato di %s secondi.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
|
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
|
||||||
"other_logins": "Key login",
|
"other_logins": "Key login",
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
"bcc_maps": "BCC kartes",
|
"bcc_maps": "BCC kartes",
|
||||||
"filters": "Filtri",
|
"filters": "Filtri",
|
||||||
"recipient_maps": "Saņēmēja kartes",
|
"recipient_maps": "Saņēmēja kartes",
|
||||||
"syncjobs": "Sinhronizācijas uzdevumi"
|
"syncjobs": "Sinhronizācijas uzdevumi",
|
||||||
|
"spam_score": "Mēstules novērtējums"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
"activate_filter_warn": "Visi pārējie filtri tiks deaktivizēti, kad aktīvs ir atzīmēts.",
|
"activate_filter_warn": "Visi pārējie filtri tiks deaktivizēti, kad aktīvs ir atzīmēts.",
|
||||||
|
@ -104,10 +105,10 @@
|
||||||
"host": "Hosts",
|
"host": "Hosts",
|
||||||
"import": "Importēt",
|
"import": "Importēt",
|
||||||
"import_private_key": "Importēt privātu atslēgu",
|
"import_private_key": "Importēt privātu atslēgu",
|
||||||
"in_use_by": "Tiek lietots ar",
|
"in_use_by": "Izmanto",
|
||||||
"inactive": "Neaktīvs",
|
"inactive": "Neaktīvs",
|
||||||
"link": "Saite",
|
"link": "Saite",
|
||||||
"loading": "Lūdzu uzgaidiet...",
|
"loading": "Lūgums uzgaidīt...",
|
||||||
"logo_info": "Jūsu attēls augšējā navigācijas joslā tiks palielināts līdz 40 pikseļiem un maks. sākumlapas platums par 250 pikseļi. Ir ļoti ieteicama pielāgojama grafikaYour image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. Ir ļoti ieteicama pielāgojamā grafika",
|
"logo_info": "Jūsu attēls augšējā navigācijas joslā tiks palielināts līdz 40 pikseļiem un maks. sākumlapas platums par 250 pikseļi. Ir ļoti ieteicama pielāgojama grafikaYour image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. Ir ļoti ieteicama pielāgojamā grafika",
|
||||||
"main_name": "\"mailcow UI\" nosaukums",
|
"main_name": "\"mailcow UI\" nosaukums",
|
||||||
"merged_vars_hint": "Pelēkās rindas tika apvienotas <code>vars.(local.)inc.php</code> un nevar tikt modificētas.",
|
"merged_vars_hint": "Pelēkās rindas tika apvienotas <code>vars.(local.)inc.php</code> un nevar tikt modificētas.",
|
||||||
|
@ -144,7 +145,10 @@
|
||||||
"ui_texts": "UI etiķetes un teksti",
|
"ui_texts": "UI etiķetes un teksti",
|
||||||
"unchanged_if_empty": "Ja nav veiktas izmaiņas, atstājiet tukšu",
|
"unchanged_if_empty": "Ja nav veiktas izmaiņas, atstājiet tukšu",
|
||||||
"upload": "Augšupielādēt",
|
"upload": "Augšupielādēt",
|
||||||
"username": "Lietotājvārds"
|
"username": "Lietotājvārds",
|
||||||
|
"generate": "izveidot",
|
||||||
|
"message": "Ziņojums",
|
||||||
|
"last_applied": "Pēdējoreiz pielietots"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Piekļuve liegta, vai nepareizi dati",
|
"access_denied": "Piekļuve liegta, vai nepareizi dati",
|
||||||
|
@ -170,7 +174,7 @@
|
||||||
"is_alias": "%s jau ir zināms alias",
|
"is_alias": "%s jau ir zināms alias",
|
||||||
"is_alias_or_mailbox": "%s jau ir zināms alias, pastkastes vai alias addrese izvērsta no alias domēna.",
|
"is_alias_or_mailbox": "%s jau ir zināms alias, pastkastes vai alias addrese izvērsta no alias domēna.",
|
||||||
"is_spam_alias": "%s ir jau zināms spam alias",
|
"is_spam_alias": "%s ir jau zināms spam alias",
|
||||||
"last_key": "Pēdējā atslēga nevar būt dzēsta",
|
"last_key": "Pēdējo atslēgu nevar izdzēst, tā vietā jāatspējo divpakāpju pārbaude.",
|
||||||
"login_failed": "Ielogošanās neveiksmīga",
|
"login_failed": "Ielogošanās neveiksmīga",
|
||||||
"mailbox_invalid": "Pastkastes vārds ir nederīgs",
|
"mailbox_invalid": "Pastkastes vārds ir nederīgs",
|
||||||
"mailbox_quota_exceeded": "Kvota pārsniedz domēna limitu (max. %d MiB)",
|
"mailbox_quota_exceeded": "Kvota pārsniedz domēna limitu (max. %d MiB)",
|
||||||
|
@ -262,7 +266,8 @@
|
||||||
"title": "Labot priekšmetu",
|
"title": "Labot priekšmetu",
|
||||||
"unchanged_if_empty": "Ja neizmainīts atstājiet tukšu",
|
"unchanged_if_empty": "Ja neizmainīts atstājiet tukšu",
|
||||||
"username": "Lietotājvārds",
|
"username": "Lietotājvārds",
|
||||||
"validate_save": "Apstiprināt un saglabāt"
|
"validate_save": "Apstiprināt un saglabāt",
|
||||||
|
"last_modified": "Pēdējoreiz mainīts"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"cancel": "Atcelt",
|
"cancel": "Atcelt",
|
||||||
|
@ -314,21 +319,21 @@
|
||||||
"bcc_destinations": "BCC galamērķi/s",
|
"bcc_destinations": "BCC galamērķi/s",
|
||||||
"bcc_info": "BCC kartes tiek izmantotas, lai klusu pārsūtītu visu ziņojumu kopijas uz citu adresi. Saņēmēja kartes tipa ieraksts tiek izmantots, kad vietējais galamērķis darbojas kā pasta adresāts. Sūtītāja kartes atbilst vienam un tam pašam principam. <br/>\r\n Vietējais galamērķis netiks informēts par piegādes neveiksmi. ",
|
"bcc_info": "BCC kartes tiek izmantotas, lai klusu pārsūtītu visu ziņojumu kopijas uz citu adresi. Saņēmēja kartes tipa ieraksts tiek izmantots, kad vietējais galamērķis darbojas kā pasta adresāts. Sūtītāja kartes atbilst vienam un tam pašam principam. <br/>\r\n Vietējais galamērķis netiks informēts par piegādes neveiksmi. ",
|
||||||
"bcc_local_dest": "Vietējais galamērķis",
|
"bcc_local_dest": "Vietējais galamērķis",
|
||||||
"bcc_map_type": "BCC tips",
|
"bcc_map_type": "BCC veids",
|
||||||
"bcc_maps": "BCC kartes",
|
"bcc_maps": "BCC kartes",
|
||||||
"bcc_rcpt_map": "saņēmēja karte",
|
"bcc_rcpt_map": "saņēmēja karte",
|
||||||
"bcc_sender_map": "Sūtītāja karte",
|
"bcc_sender_map": "Sūtītāja karte",
|
||||||
"bcc_to_rcpt": "Pārslēdzieties uz adresāta kartes tipu",
|
"bcc_to_rcpt": "Pārslēdzieties uz adresāta kartes tipu",
|
||||||
"bcc_to_sender": "Pārslēgties uz sūtītāja kartes tipu",
|
"bcc_to_sender": "Pārslēgties uz sūtītāja kartes tipu",
|
||||||
"bcc_type": "BCC tips",
|
"bcc_type": "BCC tips",
|
||||||
"deactivate": "Deaktivizēt",
|
"deactivate": "Deaktivēt",
|
||||||
"description": "Apraksts",
|
"description": "Apraksts",
|
||||||
"dkim_key_length": "DKIM atslēgas garums (bits)",
|
"dkim_key_length": "DKIM atslēgas garums (bits)",
|
||||||
"domain": "Domēns",
|
"domain": "Domēns",
|
||||||
"domain_admins": "Domēna administratori",
|
"domain_admins": "Domēna administratori",
|
||||||
"domain_aliases": "Domēna aliases",
|
"domain_aliases": "Domēna aliases",
|
||||||
"domain_quota": "Kvota",
|
"domain_quota": "Kvota",
|
||||||
"domain_quota_total": "Kopējā domēna kvota",
|
"domain_quota_total": "Kopējais domēna ierobežojums",
|
||||||
"domains": "Domēns",
|
"domains": "Domēns",
|
||||||
"edit": "Labot",
|
"edit": "Labot",
|
||||||
"empty": "Nav rezultātu",
|
"empty": "Nav rezultātu",
|
||||||
|
@ -341,7 +346,7 @@
|
||||||
"inactive": "Neaktīvs",
|
"inactive": "Neaktīvs",
|
||||||
"kind": "Veids",
|
"kind": "Veids",
|
||||||
"last_run": "Pēdējā norise",
|
"last_run": "Pēdējā norise",
|
||||||
"last_run_reset": "Nākamais grafiks",
|
"last_run_reset": "Ievietot sarakstā kā nākamo",
|
||||||
"mailbox_quota": "Maks. pastkastes izmērs",
|
"mailbox_quota": "Maks. pastkastes izmērs",
|
||||||
"mailboxes": "Pastkaste",
|
"mailboxes": "Pastkaste",
|
||||||
"max_aliases": "Maks. iespejamās aliases",
|
"max_aliases": "Maks. iespejamās aliases",
|
||||||
|
@ -374,7 +379,13 @@
|
||||||
"tls_enforce_out": "Piespiest TLS izejošajiem",
|
"tls_enforce_out": "Piespiest TLS izejošajiem",
|
||||||
"toggle_all": "Pārslēgt visu",
|
"toggle_all": "Pārslēgt visu",
|
||||||
"username": "Lietotājvārds",
|
"username": "Lietotājvārds",
|
||||||
"waiting": "Gaidīšana"
|
"waiting": "Gaidīšana",
|
||||||
|
"last_modified": "Pēdējoreiz mainīts",
|
||||||
|
"booking_0_short": "Vienmēŗ bezmaksas",
|
||||||
|
"daily": "Ik dienu",
|
||||||
|
"hourly": "Ik stundu",
|
||||||
|
"last_mail_login": "Pēdējā pieteikšanās pastkastē",
|
||||||
|
"mailbox": "Pastkaste"
|
||||||
},
|
},
|
||||||
"quarantine": {
|
"quarantine": {
|
||||||
"action": "Darbības",
|
"action": "Darbības",
|
||||||
|
@ -547,5 +558,14 @@
|
||||||
"waiting": "Waiting",
|
"waiting": "Waiting",
|
||||||
"week": "Nedēļa",
|
"week": "Nedēļa",
|
||||||
"weeks": "Nedēļas"
|
"weeks": "Nedēļas"
|
||||||
|
},
|
||||||
|
"datatables": {
|
||||||
|
"paginate": {
|
||||||
|
"first": "Pirmā",
|
||||||
|
"last": "Pēdējā"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"last_modified": "Pēdējoreiz mainīts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -598,7 +598,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Aanmelding vertraagd met %s seconden.",
|
"delayed": "Aanmelding vertraagd met %s seconden.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Aanmelden",
|
"login": "Aanmelden",
|
||||||
"mobileconfig_info": "Log in als mailboxgebruiker om het Apple-verbindingsprofiel te downloaden.",
|
"mobileconfig_info": "Log in als mailboxgebruiker om het Apple-verbindingsprofiel te downloaden.",
|
||||||
"other_logins": "Meld aan met key",
|
"other_logins": "Meld aan met key",
|
||||||
|
|
|
@ -656,7 +656,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Conectarea a fost întârziată cu %s secunde.",
|
"delayed": "Conectarea a fost întârziată cu %s secunde.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Autentificare",
|
"login": "Autentificare",
|
||||||
"mobileconfig_info": "Autentificați-vă cu adresa de email pentru a descărca profilul de conexiune Apple.",
|
"mobileconfig_info": "Autentificați-vă cu adresa de email pentru a descărca profilul de conexiune Apple.",
|
||||||
"other_logins": "Autentificare cu cheie",
|
"other_logins": "Autentificare cu cheie",
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
"add_domain_only": "Только добавить домен",
|
"add_domain_only": "Только добавить домен",
|
||||||
"add_domain_restart": "Добавить домен и перезапустить SOGo",
|
"add_domain_restart": "Добавить домен и перезапустить SOGo",
|
||||||
"alias_address": "Псевдоним/ы",
|
"alias_address": "Псевдоним/ы",
|
||||||
"alias_address_info": "<small>Укажите почтовые адреса разделенные запятыми или, если хотите пересылать все сообщения для домена владельцам псевдонима то: <code>@example.com</code>. <b>Только домены mailcow разрешены</b>.</small>",
|
"alias_address_info": "<small>Адрес(а) электронной почты (через запятую) или @example.com (для перехвата всех писем для домена). <b>только домены mailcow</b>.</small>",
|
||||||
"alias_domain": "Псевдоним домена",
|
"alias_domain": "Псевдоним домена",
|
||||||
"alias_domain_info": "<small>Действительные имена доменов, раздёленные запятыми.</small>",
|
"alias_domain_info": "<small>Действительные имена доменов, раздёленные запятыми.</small>",
|
||||||
"app_name": "Название приложения",
|
"app_name": "Название приложения",
|
||||||
|
@ -335,7 +335,8 @@
|
||||||
"username": "Имя пользователя",
|
"username": "Имя пользователя",
|
||||||
"validate_license_now": "Получить лицензию на основе GUID с сервера лицензий",
|
"validate_license_now": "Получить лицензию на основе GUID с сервера лицензий",
|
||||||
"verify": "Проверить",
|
"verify": "Проверить",
|
||||||
"yes": "✓"
|
"yes": "✓",
|
||||||
|
"queue_unban": "разблокировать"
|
||||||
},
|
},
|
||||||
"danger": {
|
"danger": {
|
||||||
"access_denied": "Доступ запрещён, или указаны неверные данные",
|
"access_denied": "Доступ запрещён, или указаны неверные данные",
|
||||||
|
@ -654,7 +655,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Вход был отложен на %s секунд.",
|
"delayed": "Вход был отложен на %s секунд.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Войти",
|
"login": "Войти",
|
||||||
"mobileconfig_info": "Пожалуйста, войдите в систему как пользователь почтового аккаунта для загрузки профиля подключения Apple.",
|
"mobileconfig_info": "Пожалуйста, войдите в систему как пользователь почтового аккаунта для загрузки профиля подключения Apple.",
|
||||||
"other_logins": "Вход с помощью ключа",
|
"other_logins": "Вход с помощью ключа",
|
||||||
|
|
|
@ -106,7 +106,8 @@
|
||||||
"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 k heslu aplikácie"
|
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie",
|
||||||
|
"tags": "Štítky"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"access": "Prístup",
|
"access": "Prístup",
|
||||||
|
@ -656,7 +657,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Prihlásenie bolo oneskorené o %s sekúnd.",
|
"delayed": "Prihlásenie bolo oneskorené o %s sekúnd.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Prihlásenie",
|
"login": "Prihlásenie",
|
||||||
"mobileconfig_info": "Prosím, prihláste sa ako mailový používateľ pre stiahnutie požadovaného Apple profilu.",
|
"mobileconfig_info": "Prosím, prihláste sa ako mailový používateľ pre stiahnutie požadovaného Apple profilu.",
|
||||||
"other_logins": "Prihlásenie kľúčom",
|
"other_logins": "Prihlásenie kľúčom",
|
||||||
|
|
|
@ -618,7 +618,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "Av säkerhetsskäl har inloggning inaktiverats i %s sekunder.",
|
"delayed": "Av säkerhetsskäl har inloggning inaktiverats i %s sekunder.",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Logga in",
|
"login": "Logga in",
|
||||||
"mobileconfig_info": "Logga in som en användare av brevlåda för att ladda ner den begärda Apple-anslutningsprofilen.",
|
"mobileconfig_info": "Logga in som en användare av brevlåda för att ladda ner den begärda Apple-anslutningsprofilen.",
|
||||||
"other_logins": "Loggain med nyckel",
|
"other_logins": "Loggain med nyckel",
|
||||||
|
|
|
@ -656,7 +656,7 @@
|
||||||
"awaiting_tfa_confirmation": "В очікуванні підтвердження TFA"
|
"awaiting_tfa_confirmation": "В очікуванні підтвердження TFA"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "Увійти",
|
"login": "Увійти",
|
||||||
"other_logins": "Вхід за допомогою ключа",
|
"other_logins": "Вхід за допомогою ключа",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
|
|
|
@ -661,7 +661,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "请在 %s 秒后重新登录。",
|
"delayed": "请在 %s 秒后重新登录。",
|
||||||
"fido2_webauthn": "使用 FIDO2/WebAuthn 登录",
|
"fido2_webauthn": "使用 FIDO2/WebAuthn Login 登录",
|
||||||
"login": "登录",
|
"login": "登录",
|
||||||
"mobileconfig_info": "请使用邮箱用户登录以下载 Apple 连接描述文件。",
|
"mobileconfig_info": "请使用邮箱用户登录以下载 Apple 连接描述文件。",
|
||||||
"other_logins": "Key 登录",
|
"other_logins": "Key 登录",
|
||||||
|
|
|
@ -655,7 +655,7 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"delayed": "請在 %s 秒後重新登入。",
|
"delayed": "請在 %s 秒後重新登入。",
|
||||||
"fido2_webauthn": "FIDO2/WebAuthn",
|
"fido2_webauthn": "FIDO2/WebAuthn Login",
|
||||||
"login": "登入",
|
"login": "登入",
|
||||||
"mobileconfig_info": "請使用信箱使用者登入以下載 Apple 連接描述檔案。",
|
"mobileconfig_info": "請使用信箱使用者登入以下載 Apple 連接描述檔案。",
|
||||||
"other_logins": "金鑰登入",
|
"other_logins": "金鑰登入",
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- /col-md-12 -->
|
</div> <!-- /col-md-12 -->
|
||||||
</div> <!-- /row -->
|
</div> <!-- /row -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include 'modals/admin.twig' %}
|
{% include 'modals/admin.twig' %}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ var lang = {{ lang_admin|raw }};
|
||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
var admin_username = '{{ mailcow_cc_username }}';
|
var admin_username = '{{ mailcow_cc_username }}';
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var pagination_size = '{{ pagination_size }}';
|
var pagination_size = Math.trunc('{{ pagination_size }}');
|
||||||
var log_pagination_size = '{{ log_pagination_size }}';
|
var log_pagination_size = Math.trunc('{{ log_pagination_size }}');
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -46,7 +46,7 @@
|
||||||
<div class="col-sm-3 col-5 text-end">{{ lang.fido2.known_ids }}:</div>
|
<div class="col-sm-3 col-5 text-end">{{ lang.fido2.known_ids }}:</div>
|
||||||
<div class="col-sm-9 col-7">
|
<div class="col-sm-9 col-7">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-hover table-condensed" id="fido2_keys">
|
<table class="table table-striped table-hover table-condensed w-100" id="fido2_keys">
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th style="min-width:240px;text-align: right">{{ lang.admin.action }}</th>
|
<th style="min-width:240px;text-align: right">{{ lang.admin.action }}</th>
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
var lang_user = {{ lang_user|raw }};
|
var lang_user = {{ lang_user|raw }};
|
||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var pagination_size = '{{ pagination_size }}';
|
var pagination_size = Math.trunc('{{ pagination_size }}');
|
||||||
var table_for_domain = '{{ domain }}';
|
var table_for_domain = '{{ domain }}';
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -38,15 +38,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex mt-4" style="position: relative">
|
<div class="d-flex mt-4" style="position: relative">
|
||||||
<div class="btn-group">
|
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button>
|
||||||
<div class="btn-group">
|
<button type="button" class="btn btn-xs-lg btn-success ms-2" id="fido2-login"><i class="bi bi-shield-fill-check"></i> {{ lang.login.fido2_webauthn }}</button>
|
||||||
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button>
|
|
||||||
<button type="button" class="btn btn-xs-lg btn-success dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li><a class="dropdown-item" href="#" id="fido2-login"><i class="bi bi-shield-fill-check"></i> {{ lang.login.fido2_webauthn }}</a></li>
|
|
||||||
</ul>
|
|
||||||
</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-secondary ms-auto dropdown-toggle" data-bs-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-secondary ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
|
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
var lang_rl = {{ lang_rl|raw }};
|
var lang_rl = {{ lang_rl|raw }};
|
||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var pagination_size = '{{ pagination_size }}';
|
var pagination_size = Math.trunc('{{ pagination_size }}');
|
||||||
var role = '{{ role }}';
|
var role = '{{ role }}';
|
||||||
var is_dual = {{ is_dual }};
|
var is_dual = {{ is_dual }};
|
||||||
var ALLOW_ADMIN_EMAIL_LOGIN = {{ allow_admin_email_login }};
|
var ALLOW_ADMIN_EMAIL_LOGIN = {{ allow_admin_email_login }};
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<table id="quarantinetable" class="table table-striped"></table>
|
<table id="quarantinetable" class="table table-striped w-100"></table>
|
||||||
<div class="mass-actions-quarantine mt-4">
|
<div class="mass-actions-quarantine mt-4">
|
||||||
<div class="btn-group" data-acl="{{ acl.quarantine }}">
|
<div class="btn-group" data-acl="{{ acl.quarantine }}">
|
||||||
<a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="qitems" href="#"><i class="bi bi-check-all"></i> {{ lang.quarantine.toggle_all }}</a>
|
<a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="qitems" href="#"><i class="bi bi-check-all"></i> {{ lang.quarantine.toggle_all }}</a>
|
||||||
|
@ -66,7 +66,7 @@ var acl = '{{ acl_json|raw }}';
|
||||||
var lang = {{ lang_quarantine|raw }};
|
var lang = {{ lang_quarantine|raw }};
|
||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var pagination_size = '{{ pagination_size }}';
|
var pagination_size = Math.trunc('{{ pagination_size }}');
|
||||||
var role = '{{ role }}';
|
var role = '{{ role }}';
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
var lang = {{ lang_queue|raw }};
|
var lang = {{ lang_queue|raw }};
|
||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var pagination_size = '{{ pagination_size }}';
|
var pagination_size = Math.trunc('{{ pagination_size }}');
|
||||||
var table_for_domain = '{{ domain }}';
|
var table_for_domain = '{{ domain }}';
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
var acl = '{{ acl_json|raw }}';
|
var acl = '{{ acl_json|raw }}';
|
||||||
var lang = {{ lang_user|raw }};
|
var lang = {{ lang_user|raw }};
|
||||||
var csrf_token = '{{ csrf_token }}';
|
var csrf_token = '{{ csrf_token }}';
|
||||||
var pagination_size = '{{ pagination_size }}';
|
var pagination_size = Math.trunc('{{ pagination_size }}');
|
||||||
var mailcow_cc_username = '{{ mailcow_cc_username }}';
|
var mailcow_cc_username = '{{ mailcow_cc_username }}';
|
||||||
var user_spam_score = [{{ user_spam_score }}];
|
var user_spam_score = [{{ user_spam_score }}];
|
||||||
var lang_datatables = {{ lang_datatables|raw }};
|
var lang_datatables = {{ lang_datatables|raw }};
|
||||||
|
|
|
@ -20,6 +20,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
|
||||||
'tfa_data' => $tfa_data,
|
'tfa_data' => $tfa_data,
|
||||||
'fido2_data' => $fido2_data,
|
'fido2_data' => $fido2_data,
|
||||||
'lang_user' => json_encode($lang['user']),
|
'lang_user' => json_encode($lang['user']),
|
||||||
|
'lang_datatables' => json_encode($lang['datatables']),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') {
|
elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') {
|
||||||
|
|
|
@ -64,7 +64,7 @@ services:
|
||||||
- redis
|
- redis
|
||||||
|
|
||||||
clamd-mailcow:
|
clamd-mailcow:
|
||||||
image: mailcow/clamd:1.60
|
image: mailcow/clamd:1.61
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- unbound-mailcow
|
- unbound-mailcow
|
||||||
|
@ -116,7 +116,7 @@ services:
|
||||||
- rspamd
|
- rspamd
|
||||||
|
|
||||||
php-fpm-mailcow:
|
php-fpm-mailcow:
|
||||||
image: mailcow/phpfpm:1.81
|
image: mailcow/phpfpm:1.82
|
||||||
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
|
||||||
|
@ -181,7 +181,7 @@ services:
|
||||||
- phpfpm
|
- phpfpm
|
||||||
|
|
||||||
sogo-mailcow:
|
sogo-mailcow:
|
||||||
image: mailcow/sogo:1.113
|
image: mailcow/sogo:1.115
|
||||||
environment:
|
environment:
|
||||||
- DBNAME=${DBNAME}
|
- DBNAME=${DBNAME}
|
||||||
- DBUSER=${DBUSER}
|
- DBUSER=${DBUSER}
|
||||||
|
@ -230,7 +230,7 @@ services:
|
||||||
- sogo
|
- sogo
|
||||||
|
|
||||||
dovecot-mailcow:
|
dovecot-mailcow:
|
||||||
image: mailcow/dovecot:1.21
|
image: mailcow/dovecot:1.22
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql-mailcow
|
- mysql-mailcow
|
||||||
dns:
|
dns:
|
||||||
|
@ -411,7 +411,7 @@ services:
|
||||||
acme-mailcow:
|
acme-mailcow:
|
||||||
depends_on:
|
depends_on:
|
||||||
- nginx-mailcow
|
- nginx-mailcow
|
||||||
image: mailcow/acme:1.83
|
image: mailcow/acme:1.84
|
||||||
dns:
|
dns:
|
||||||
- ${IPV4_NETWORK:-172.22.1}.254
|
- ${IPV4_NETWORK:-172.22.1}.254
|
||||||
environment:
|
environment:
|
||||||
|
@ -449,7 +449,7 @@ services:
|
||||||
- acme
|
- acme
|
||||||
|
|
||||||
netfilter-mailcow:
|
netfilter-mailcow:
|
||||||
image: mailcow/netfilter:1.50
|
image: mailcow/netfilter:1.51
|
||||||
stop_grace_period: 30s
|
stop_grace_period: 30s
|
||||||
depends_on:
|
depends_on:
|
||||||
- dovecot-mailcow
|
- dovecot-mailcow
|
||||||
|
@ -538,11 +538,10 @@ services:
|
||||||
- watchdog
|
- watchdog
|
||||||
|
|
||||||
dockerapi-mailcow:
|
dockerapi-mailcow:
|
||||||
image: mailcow/dockerapi:2.0
|
image: mailcow/dockerapi:2.01
|
||||||
security_opt:
|
security_opt:
|
||||||
- label=disable
|
- label=disable
|
||||||
restart: always
|
restart: always
|
||||||
oom_kill_disable: true
|
|
||||||
dns:
|
dns:
|
||||||
- ${IPV4_NETWORK:-172.22.1}.254
|
- ${IPV4_NETWORK:-172.22.1}.254
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -56,9 +56,8 @@ done
|
||||||
|
|
||||||
MAILCOW_DOCKER_COMPOSE=${MAILCOW_DOCKER_COMPOSE:-"docker-compose"}
|
MAILCOW_DOCKER_COMPOSE=${MAILCOW_DOCKER_COMPOSE:-"docker-compose"}
|
||||||
|
|
||||||
if [[ "${CONTAINER_ENGINE}" == "docker" ]] && command -v docker compose > /dev/null 2>&1; then
|
if [[ "${CONTAINER_ENGINE}" == "docker" ]] && docker compose > /dev/null 2>&1; then
|
||||||
version=$(docker compose version --short)
|
if docker compose version --short | grep "^2." > /dev/null 2>&1; then
|
||||||
if [[ $version =~ ^2\.([0-9]+)\.([0-9]+) ]]; then
|
|
||||||
COMPOSE_VERSION=native
|
COMPOSE_VERSION=native
|
||||||
echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
|
echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
|
||||||
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
|
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
|
||||||
|
@ -66,12 +65,12 @@ if [[ "${CONTAINER_ENGINE}" == "docker" ]] && command -v docker compose > /dev/n
|
||||||
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually! \e[0m"
|
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually! \e[0m"
|
||||||
else
|
else
|
||||||
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
||||||
echo -e "\e[31mPlease update/install manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
elif command -v $MAILCOW_DOCKER_COMPOSE > /dev/null 2>&1; then
|
elif $MAILCOW_DOCKER_COMPOSE > /dev/null 2>&1; then
|
||||||
version=$($MAILCOW_DOCKER_COMPOSE version --short)
|
if ! [[ $(alias $MAILCOW_DOCKER_COMPOSE 2> /dev/null) ]] ; then
|
||||||
if [[ $version =~ ^2\.([0-9]+)\.([0-9]+) ]]; then
|
if $MAILCOW_DOCKER_COMPOSE version --short | grep "^2." > /dev/null 2>&1; then
|
||||||
COMPOSE_VERSION=standalone
|
COMPOSE_VERSION=standalone
|
||||||
echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
|
echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
|
||||||
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
|
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
|
||||||
|
@ -82,9 +81,11 @@ elif command -v $MAILCOW_DOCKER_COMPOSE > /dev/null 2>&1; then
|
||||||
echo -e "\e[31mPlease update/install manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease update/install manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
echo -e "\e[31mCannot find Docker Compose.\e[0m"
|
echo -e "\e[31mCannot find Docker Compose.\e[0m"
|
||||||
echo -e "\e[31mPlease install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease install it regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -520,6 +521,15 @@ case ${git_branch} in
|
||||||
mailcow_last_git_version=""
|
mailcow_last_git_version=""
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
# if [ ${git_branch} == "master" ]; then
|
||||||
|
# mailcow_git_version=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||||
|
# elif [ ${git_branch} == "nightly" ]; then
|
||||||
|
# mailcow_git_version=$(git rev-parse --short $(git rev-parse @{upstream}))
|
||||||
|
# mailcow_last_git_version=""
|
||||||
|
# else
|
||||||
|
# mailcow_git_version=$(git rev-parse --short HEAD)
|
||||||
|
# mailcow_last_git_version=""
|
||||||
|
# fi
|
||||||
|
|
||||||
if [[ $SKIP_BRANCH != "y" ]]; then
|
if [[ $SKIP_BRANCH != "y" ]]; then
|
||||||
mailcow_git_commit=$(git rev-parse origin/${git_branch})
|
mailcow_git_commit=$(git rev-parse origin/${git_branch})
|
||||||
|
|
|
@ -26,6 +26,6 @@ services:
|
||||||
- /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysqld.sock
|
- /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysqld.sock
|
||||||
|
|
||||||
mysql-mailcow:
|
mysql-mailcow:
|
||||||
image: alpine:3.10
|
image: alpine:3.17
|
||||||
command: /bin/true
|
command: /bin/true
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
|
# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
|
||||||
NEXTCLOUD_VERSION=25.0.2
|
NEXTCLOUD_VERSION=25.0.4
|
||||||
|
|
||||||
for bin in curl dirmngr; do
|
echo -ne "Checking prerequisites..."
|
||||||
if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
|
sleep 1
|
||||||
|
for bin in curl dirmngr tar bzip2; do
|
||||||
|
if [[ -z $(which ${bin}) ]]; then echo -ne "\r\033[31mCannot find ${bin}, exiting...\033[0m\n"; exit 1; fi
|
||||||
done
|
done
|
||||||
|
echo -ne "\r\033[32mFound all prerequisites! Continuing...\033[0m\n"
|
||||||
|
|
||||||
[[ -z ${1} ]] && NC_HELP=y
|
[[ -z ${1} ]] && NC_HELP=y
|
||||||
|
|
||||||
while [ "$1" != '' ]; do
|
while [ "$1" != '' ]; do
|
||||||
|
if [[ $# -ne 1 ]]; then
|
||||||
|
echo -e "\033[31mPlease use only one parameter at the same time!\033[0m" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
case "${1}" in
|
case "${1}" in
|
||||||
-p|--purge) NC_PURGE=y && shift;;
|
-p|--purge) NC_PURGE=y && shift;;
|
||||||
-i|--install) NC_INSTALL=y && shift;;
|
-i|--install) NC_INSTALL=y && shift;;
|
||||||
|
-u|--update) NC_UPDATE=y && shift;;
|
||||||
-r|--resetpw) NC_RESETPW=y && shift;;
|
-r|--resetpw) NC_RESETPW=y && shift;;
|
||||||
-h|--help) NC_HELP=y && shift;;
|
-h|--help) NC_HELP=y && shift;;
|
||||||
*) echo "Unknown parameter: ${1}" && shift;;
|
*) echo "Unknown parameter: ${1}" && shift;;
|
||||||
|
@ -22,13 +30,11 @@ if [[ ${NC_HELP} == "y" ]]; then
|
||||||
printf 'Usage:\n\n'
|
printf 'Usage:\n\n'
|
||||||
printf ' -p|--purge\n Purge Nextcloud\n'
|
printf ' -p|--purge\n Purge Nextcloud\n'
|
||||||
printf ' -i|--install\n Install Nextcloud\n'
|
printf ' -i|--install\n Install Nextcloud\n'
|
||||||
|
printf ' -u|--update\n Update Nextcloud\n'
|
||||||
printf ' -r|--resetpw\n Reset password\n\n'
|
printf ' -r|--resetpw\n Reset password\n\n'
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[[ ${NC_PURGE} == "y" ]] && [[ ${NC_INSTALL} == "y" ]] && { echo "Cannot use -p and -i at the same time!"; exit 1; }
|
|
||||||
[[ ${NC_PURGE} == "y" ]] && [[ ${NC_RESETPW} == "y" ]] && { echo "Cannot use -p and -r at the same time!"; exit 1; }
|
|
||||||
|
|
||||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
cd ${SCRIPT_DIR}/../
|
cd ${SCRIPT_DIR}/../
|
||||||
source mailcow.conf
|
source mailcow.conf
|
||||||
|
@ -41,8 +47,27 @@ if [[ ${NC_PURGE} == "y" ]]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e \
|
echo -e "\033[33mDetecting Database information...\033[0m"
|
||||||
"$(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "SELECT IFNULL(GROUP_CONCAT('DROP TABLE ', TABLE_SCHEMA, '.', TABLE_NAME SEPARATOR ';'),'SELECT NULL;') FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'oc_%' AND TABLE_SCHEMA = '${DBNAME}';" -BN)"
|
if [[ $(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "Show databases" | grep "nextcloud") ]]; then
|
||||||
|
echo -e "\033[32mFound seperate Nextcloud database (newer scheme)!\033[0m"
|
||||||
|
echo -e "\033[31mPurging...\033[0m"
|
||||||
|
docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "DROP DATABASE nextcloud;" > /dev/null
|
||||||
|
docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "DROP USER 'nextcloud'@'%';" > /dev/null
|
||||||
|
elif [[ $(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} mailcow -e "SHOW TABLES LIKE 'oc_%'") && $? -eq 0 ]]; then
|
||||||
|
echo -e "\033[32mFound Nextcloud (oc) tables inside of mailcow database (old scheme)!\033[0m"
|
||||||
|
echo -e "\033[31mPurging...\033[0m"
|
||||||
|
docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e \
|
||||||
|
"$(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "SELECT IFNULL(GROUP_CONCAT('DROP TABLE ', TABLE_SCHEMA, '.', TABLE_NAME SEPARATOR ';'),'SELECT NULL;') FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'oc_%' AND TABLE_SCHEMA = '${DBNAME}';" -BN)" > /dev/null
|
||||||
|
elif [[ $(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} mailcow -e "SHOW TABLES LIKE 'nc_%'") && $? -eq 0 ]]; then
|
||||||
|
echo -e "\033[32mFound Nextcloud (nc) tables inside of mailcow database (old scheme)!\033[0m"
|
||||||
|
echo -e "\033[31mPurging...\033[0m"
|
||||||
|
docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e \
|
||||||
|
"$(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "SELECT IFNULL(GROUP_CONCAT('DROP TABLE ', TABLE_SCHEMA, '.', TABLE_NAME SEPARATOR ';'),'SELECT NULL;') FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'nc_%' AND TABLE_SCHEMA = '${DBNAME}';" -BN)" > /dev/null
|
||||||
|
else
|
||||||
|
echo -e "\033[31mError: No Nextcloud databases/tables found!"
|
||||||
|
echo -e "\033[33mNot purging anything...\033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
docker exec -it $(docker ps -f name=redis-mailcow -q) /bin/sh -c ' cat <<EOF | redis-cli
|
docker exec -it $(docker ps -f name=redis-mailcow -q) /bin/sh -c ' cat <<EOF | redis-cli
|
||||||
SELECT 10
|
SELECT 10
|
||||||
FLUSHDB
|
FLUSHDB
|
||||||
|
@ -58,9 +83,10 @@ EOF
|
||||||
|
|
||||||
docker restart $(docker ps -aqf name=nginx-mailcow)
|
docker restart $(docker ps -aqf name=nginx-mailcow)
|
||||||
|
|
||||||
|
echo -e "\033[32mNextcloud has been uninstalled sucessfully!\033[0m"
|
||||||
|
|
||||||
elif [[ ${NC_UPDATE} == "y" ]]; then
|
elif [[ ${NC_UPDATE} == "y" ]]; then
|
||||||
exit;
|
read -r -p "Are you sure you want to update Nextcloud (with Nextclouds own updater)? [y/N] " response
|
||||||
read -r -p "Are you sure you want to update Nextcloud? [y/N] " response
|
|
||||||
response=${response,,}
|
response=${response,,}
|
||||||
if [[ ! "$response" =~ ^(yes|y)$ ]]; then
|
if [[ ! "$response" =~ ^(yes|y)$ ]]; then
|
||||||
echo "OK, aborting."
|
echo "OK, aborting."
|
||||||
|
@ -68,23 +94,14 @@ elif [[ ${NC_UPDATE} == "y" ]]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f data/web/nextcloud/occ ]; then
|
if [ ! -f data/web/nextcloud/occ ]; then
|
||||||
echo "Nextcloud occ not found. Is Nextcloud installed?"
|
echo -e "\033[31mError: Nextcloud occ not found. Is Nextcloud installed?\033[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! grep -q 'installed: true' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then
|
if ! grep -q 'installed: true' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then
|
||||||
echo "Nextcloud seems not to be installed."
|
echo "Nextcloud seems not to be installed."
|
||||||
exit 1
|
exit 1
|
||||||
elif ! grep -q 'version: 20\.' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then
|
|
||||||
echo "Cannot upgrade to new major version, please update manually."
|
|
||||||
exit 1
|
|
||||||
else
|
else
|
||||||
curl -L# -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/nextcloud-$NEXTCLOUD_VERSION.tar.bz2" || { echo "Failed to download Nextcloud archive."; exit 1; } \
|
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "php /web/nextcloud/updater/updater.phar"
|
||||||
&& tar -xjf nextcloud.tar.bz2 -C ./data/web/ \
|
|
||||||
&& rm nextcloud.tar.bz2 \
|
|
||||||
&& mkdir -p ./data/web/nextcloud/data \
|
|
||||||
&& chmod +x ./data/web/nextcloud/occ \
|
|
||||||
docker exec -it $(docker ps -f name=php-fpm-mailcow -q) bash -c "chown www-data:www-data -R /web/nextcloud" \
|
|
||||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings upgrade"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
elif [[ ${NC_INSTALL} == "y" ]]; then
|
elif [[ ${NC_INSTALL} == "y" ]]; then
|
||||||
|
@ -97,25 +114,48 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
|
||||||
[[ ! ${NC_CONT_FAIL,,} =~ ^(yes|y)$ ]] && { echo "Ok, exiting..."; exit 1; }
|
[[ ! ${NC_CONT_FAIL,,} =~ ^(yes|y)$ ]] && { echo "Ok, exiting..."; exit 1; }
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ADMIN_NC_PASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
|
echo -e "\033[33mDownloading \033[34mNextcloud ${NEXTCLOUD_VERSION}\033[33m...\033[0m"
|
||||||
|
|
||||||
curl -L# -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/nextcloud-$NEXTCLOUD_VERSION.tar.bz2" || { echo "Failed to download Nextcloud archive."; exit 1; } \
|
curl -L# -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/nextcloud-$NEXTCLOUD_VERSION.tar.bz2" || { echo "Failed to download Nextcloud archive."; exit 1; } \
|
||||||
&& tar -xjf nextcloud.tar.bz2 -C ./data/web/ \
|
&& tar -xjf nextcloud.tar.bz2 -C ./data/web/ \
|
||||||
&& rm nextcloud.tar.bz2 \
|
&& rm nextcloud.tar.bz2 \
|
||||||
&& mkdir -p ./data/web/nextcloud/data \
|
&& mkdir -p ./data/web/nextcloud/data \
|
||||||
&& chmod +x ./data/web/nextcloud/occ
|
&& chmod +x ./data/web/nextcloud/occ
|
||||||
|
|
||||||
|
echo -e "\033[33mCreating 'nextcloud' database...\033[0m"
|
||||||
|
NC_DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
|
||||||
|
NC_DBUSER=nextcloud
|
||||||
|
NC_DBNAME=nextcloud
|
||||||
|
|
||||||
|
echo -ne "[1/3] Creating 'nextcloud' database"
|
||||||
|
docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "CREATE DATABASE ${NC_DBNAME};"
|
||||||
|
sleep 2
|
||||||
|
echo -ne "\r[2/3] Creating 'nextcloud' database user"
|
||||||
|
docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "CREATE USER '${NC_DBUSER}'@'%' IDENTIFIED BY '${NC_DBPASS}';"
|
||||||
|
sleep 2
|
||||||
|
echo -ne "\r[3/3] Granting 'nextcloud' user all permissions on database 'nextcloud'"
|
||||||
|
docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "GRANT ALL PRIVILEGES ON ${NC_DBNAME}.* TO '${NC_DBUSER}'@'%';"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "\033[33mInstalling Nextcloud...\033[0m"
|
||||||
|
ADMIN_NC_PASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
|
||||||
|
|
||||||
|
echo -ne "[1/4] Setting correct permissions for www-data"
|
||||||
docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud"
|
docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud"
|
||||||
|
sleep 2
|
||||||
|
echo -ne "\r[2/4] Running occ maintenance:install to install Nextcloud"
|
||||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ --no-warnings maintenance:install \
|
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ --no-warnings maintenance:install \
|
||||||
--database mysql \
|
--database mysql \
|
||||||
--database-host mysql \
|
--database-host mysql \
|
||||||
--database-name ${DBNAME} \
|
--database-name ${NC_DBNAME} \
|
||||||
--database-user ${DBUSER} \
|
--database-user ${NC_DBUSER} \
|
||||||
--database-pass ${DBPASS} \
|
--database-pass ${NC_DBPASS} \
|
||||||
--admin-user admin \
|
--admin-user admin \
|
||||||
--admin-pass ${ADMIN_NC_PASS} \
|
--admin-pass ${ADMIN_NC_PASS} \
|
||||||
--data-dir /web/nextcloud/data
|
--data-dir /web/nextcloud/data > /dev/null 2>&1
|
||||||
|
|
||||||
|
echo -ne "\r[3/4] Setting custom parameters inside the Nextcloud config file"
|
||||||
|
echo ""
|
||||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings config:system:set redis host --value=redis --type=string; \
|
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings config:system:set redis host --value=redis --type=string; \
|
||||||
/web/nextcloud/occ --no-warnings config:system:set redis port --value=6379 --type=integer; \
|
/web/nextcloud/occ --no-warnings config:system:set redis port --value=6379 --type=integer; \
|
||||||
/web/nextcloud/occ --no-warnings config:system:set redis timeout --value=0.0 --type=integer; \
|
/web/nextcloud/occ --no-warnings config:system:set redis timeout --value=0.0 --type=integer; \
|
||||||
|
@ -141,13 +181,28 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
|
||||||
#/web/nextcloud/occ --no-warnings config:system:set user_backends 0 arguments 0 --value={dovecot:143/imap/tls/novalidate-cert}; \
|
#/web/nextcloud/occ --no-warnings config:system:set user_backends 0 arguments 0 --value={dovecot:143/imap/tls/novalidate-cert}; \
|
||||||
#/web/nextcloud/occ --no-warnings config:system:set user_backends 0 class --value=OC_User_IMAP; \
|
#/web/nextcloud/occ --no-warnings config:system:set user_backends 0 class --value=OC_User_IMAP; \
|
||||||
|
|
||||||
|
echo -e "\r[4/4] Enabling Nginx Configuration"
|
||||||
cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/
|
cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/
|
||||||
sed -i "s/NC_SUBD/${NC_SUBD}/g" ./data/conf/nginx/nextcloud.conf
|
sed -i "s/NC_SUBD/${NC_SUBD}/g" ./data/conf/nginx/nextcloud.conf
|
||||||
|
sleep 2
|
||||||
|
|
||||||
echo "Restarting Nginx..."
|
echo ""
|
||||||
|
echo -e "\033[33mFinalizing installation...\033[0m"
|
||||||
docker restart $(docker ps -aqf name=nginx-mailcow)
|
docker restart $(docker ps -aqf name=nginx-mailcow)
|
||||||
|
|
||||||
echo "Login as admin with password: ${ADMIN_NC_PASS}"
|
echo ""
|
||||||
|
echo "******************************************"
|
||||||
|
echo "* SAVE THESE CREDENTIALS *"
|
||||||
|
echo "* INSTALL DATE: $(date +%Y-%m-%d_%H-%M-%S) *"
|
||||||
|
echo "******************************************"
|
||||||
|
echo ""
|
||||||
|
echo -e "\033[36mDatabase name: ${NC_DBNAME}\033[0m"
|
||||||
|
echo -e "\033[36mDatabase user: ${NC_DBUSER}\033[0m"
|
||||||
|
echo -e "\033[36mDatabase password: ${NC_DBPASS}\033[0m"
|
||||||
|
echo ""
|
||||||
|
echo -e "\033[31mUI admin password: ${ADMIN_NC_PASS}\033[0m"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
|
||||||
elif [[ ${NC_RESETPW} == "y" ]]; then
|
elif [[ ${NC_RESETPW} == "y" ]]; then
|
||||||
printf 'You are about to set a new password for a Nextcloud user.\n\nDo not use this option if your Nextcloud is configured to use mailcow for authentication.\nSet a new password for the corresponding mailbox in mailcow, instead.\n\n'
|
printf 'You are about to set a new password for a Nextcloud user.\n\nDo not use this option if your Nextcloud is configured to use mailcow for authentication.\nSet a new password for the corresponding mailbox in mailcow, instead.\n\n'
|
||||||
|
@ -163,5 +218,4 @@ elif [[ ${NC_RESETPW} == "y" ]]; then
|
||||||
read -p "Enter the username: " NC_USER
|
read -p "Enter the username: " NC_USER
|
||||||
done
|
done
|
||||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ user:resetpassword ${NC_USER}
|
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ user:resetpassword ${NC_USER}
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -23,9 +23,8 @@ function validate_input()
|
||||||
function detect_docker_compose_command()
|
function detect_docker_compose_command()
|
||||||
{
|
{
|
||||||
if ! [[ "${DOCKER_COMPOSE_VERSION}" == "native" ]] && ! [[ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]]; then
|
if ! [[ "${DOCKER_COMPOSE_VERSION}" == "native" ]] && ! [[ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]]; then
|
||||||
if command -v docker compose > /dev/null 2>&1; then
|
if docker compose > /dev/null 2>&1; then
|
||||||
version=$(docker compose version --short)
|
if docker compose version --short | grep "2." > /dev/null 2>&1; then
|
||||||
if [[ $version =~ ^2\.([0-9]+)\.([0-9]+) ]]; then
|
|
||||||
DOCKER_COMPOSE_VERSION=native
|
DOCKER_COMPOSE_VERSION=native
|
||||||
COMPOSE_COMMAND="docker compose"
|
COMPOSE_COMMAND="docker compose"
|
||||||
echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
|
echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
|
||||||
|
@ -37,19 +36,20 @@ function detect_docker_compose_command()
|
||||||
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
elif command -v docker-compose > /dev/null 2>&1; then
|
elif docker-compose > /dev/null 2>&1; then
|
||||||
version=$(docker-compose version --short)
|
if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then
|
||||||
if [[ $version =~ ^2\.([0-9]+)\.([0-9]+) ]]; then
|
if docker-compose version --short | grep "^2." > /dev/null 2>&1; then
|
||||||
DOCKER_COMPOSE_VERSION=standalone
|
DOCKER_COMPOSE_VERSION=standalone
|
||||||
COMPOSE_COMMAND="docker-compose"
|
COMPOSE_COMMAND="docker-compose"
|
||||||
echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
|
echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
|
||||||
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
|
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
|
||||||
sleep 2
|
sleep 2
|
||||||
echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
|
echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
|
||||||
else
|
else
|
||||||
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
||||||
echo -e "\e[31mPlease update/install regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
echo -e "\e[31mPlease update/install regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
||||||
exit 1
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue