mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-02-19 15:46:23 +00:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2024cda560 | ||
|
|
03aaf4ad76 | ||
|
|
550b88861f | ||
|
|
02ae5fa007 | ||
|
|
d81f105ed7 | ||
|
|
d3ed225675 | ||
|
|
9ffc83f0f6 | ||
|
|
981c7d5974 | ||
|
|
5da089ccd7 | ||
|
|
91e00f7d97 | ||
|
|
3a675fb541 | ||
|
|
9a5d8d2d22 | ||
|
|
de812221ef | ||
|
|
f68a28fa2b | ||
|
|
7b7798e8c4 | ||
|
|
b3ac94115e | ||
|
|
b1a172cad9 | ||
|
|
f2e21c68d0 | ||
|
|
8b784c0eb1 | ||
|
|
bc59f32b96 | ||
|
|
a4fa8a4fae | ||
|
|
f730192c98 | ||
|
|
f994501296 | ||
|
|
9c3e73606c | ||
|
|
5619e16b70 | ||
|
|
d2e3867893 | ||
|
|
979f5475c3 | ||
|
|
5a10f2dd7c | ||
|
|
a80b5b7dd0 | ||
|
|
392967d664 | ||
|
|
3b3c2b7141 | ||
|
|
f55c3c0887 | ||
|
|
f423ad77f3 | ||
|
|
8ba1e1ba9e | ||
|
|
55576084fc | ||
|
|
03311b06c9 | ||
|
|
b5c3d01834 | ||
|
|
f398ecbe39 | ||
|
|
8f1ae0f099 | ||
|
|
c8bee57732 | ||
|
|
85641794c3 | ||
|
|
849decaa59 | ||
|
|
6e88550f92 | ||
|
|
7c52483887 | ||
|
|
0aa520c030 | ||
|
|
548999f163 | ||
|
|
63df547306 | ||
|
|
547d2ca308 | ||
|
|
46b995f9e3 | ||
|
|
1fdf704cb4 | ||
|
|
5ec9c4c750 | ||
|
|
afed94cc0e | ||
|
|
6f48c5ace0 | ||
|
|
9a7e1c2b5a | ||
|
|
2ef7539d55 | ||
|
|
4e52542e33 | ||
|
|
a1895ad924 | ||
|
|
d5a2c96887 | ||
|
|
3f30fe3113 | ||
|
|
d89f24a1a3 | ||
|
|
413354ff29 | ||
|
|
a28ba5bebb | ||
|
|
b93375b671 | ||
|
|
b568a33581 | ||
|
|
b05ef8edac | ||
|
|
015f9b663f | ||
|
|
b6167257c9 | ||
|
|
687fe044b2 | ||
|
|
cfa47eb873 | ||
|
|
7079000ee0 | ||
|
|
f60c4f39ee | ||
|
|
473713219f | ||
|
|
03ed81dc3f | ||
|
|
53543ccf26 | ||
|
|
3b183933e3 | ||
|
|
6c6fde8e2e | ||
|
|
61e23b6b81 | ||
|
|
6c649debc9 | ||
|
|
87b0683f77 | ||
|
|
59c1e7a18a | ||
|
|
4f9dad5dd3 | ||
|
|
adc6a0054c | ||
|
|
8a70cdb48b | ||
|
|
bb4bc11383 | ||
|
|
a366494c34 | ||
|
|
99de302ec9 | ||
|
|
907912046f | ||
|
|
2c0d379dc5 |
2
.github/renovate.json
vendored
2
.github/renovate.json
vendored
@@ -12,7 +12,7 @@
|
||||
"baseBranches": ["staging"],
|
||||
"enabledManagers": ["github-actions", "regex", "docker-compose"],
|
||||
"ignorePaths": [
|
||||
"data\/web\/inc\/lib\/vendor\/matthiasmullie\/minify\/**"
|
||||
"data\/web\/inc\/lib\/vendor\/**"
|
||||
],
|
||||
"regexManagers": [
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
if: github.event.pull_request.base.ref != 'staging' #check if the target branch is not staging
|
||||
steps:
|
||||
- name: Send message
|
||||
uses: thollander/actions-comment-pull-request@v2.4.2
|
||||
uses: thollander/actions-comment-pull-request@v2.4.3
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
|
||||
message: |
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
[](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
||||
[](https://twitter.com/mailcow_email)
|
||||

|
||||
|
||||
|
||||
## Want to support mailcow?
|
||||
|
||||
@@ -25,7 +27,9 @@ Please see [the official documentation](https://docs.mailcow.email/) for install
|
||||
|
||||
[Telegram mailcow Off-Topic channel](https://t.me/mailcowOfftopic)
|
||||
|
||||
[Official Twitter Account](https://twitter.com/mailcow_email)
|
||||
[Official 𝕏 (Twitter) Account](https://twitter.com/mailcow_email)
|
||||
|
||||
[Official Mastodon Account](https://mailcow.social/@doncow)
|
||||
|
||||
Telegram desktop clients are available for [multiple platforms](https://desktop.telegram.org). You can search the groups history for keywords.
|
||||
|
||||
@@ -38,4 +42,4 @@ mailcow is a registered word mark of The Infrastructure Company GmbH, Parkstr. 4
|
||||
|
||||
The project is managed and maintained by The Infrastructure Company GmbH.
|
||||
|
||||
Originated from @andryyy (André)
|
||||
Originated from @andryyy (André)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM clamav/clamav:1.0.3_base
|
||||
|
||||
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
||||
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
RUN apk upgrade --no-cache \
|
||||
&& apk add --update --no-cache \
|
||||
|
||||
@@ -2,9 +2,9 @@ FROM debian:bullseye-slim
|
||||
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
# renovate: datasource=github-tags depName=dovecot/core versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
# renovate: datasource=github-tags depName=dovecot/core versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||
ARG DOVECOT=2.3.21
|
||||
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||
ARG GOSU_VERSION=1.16
|
||||
ENV LC_ALL C
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ my $sth = $dbh->prepare("SELECT id,
|
||||
custom_params,
|
||||
subscribeall,
|
||||
timeout1,
|
||||
timeout2
|
||||
timeout2,
|
||||
dry
|
||||
FROM imapsync
|
||||
WHERE active = 1
|
||||
AND is_running = 0
|
||||
@@ -111,13 +112,16 @@ while ($row = $sth->fetchrow_arrayref()) {
|
||||
$subscribeall = @$row[18];
|
||||
$timeout1 = @$row[19];
|
||||
$timeout2 = @$row[20];
|
||||
$dry = @$row[21];
|
||||
|
||||
if ($enc1 eq "TLS") { $enc1 = "--tls1"; } elsif ($enc1 eq "SSL") { $enc1 = "--ssl1"; } else { undef $enc1; }
|
||||
|
||||
my $template = $run_dir . '/imapsync.XXXXXXX';
|
||||
my $passfile1 = File::Temp->new(TEMPLATE => $template);
|
||||
my $passfile2 = File::Temp->new(TEMPLATE => $template);
|
||||
|
||||
|
||||
binmode( $passfile1, ":utf8" );
|
||||
|
||||
print $passfile1 "$password1\n";
|
||||
print $passfile2 trim($master_pass) . "\n";
|
||||
|
||||
@@ -148,6 +152,7 @@ while ($row = $sth->fetchrow_arrayref()) {
|
||||
"--host2", "localhost",
|
||||
"--user2", $user2 . '*' . trim($master_user),
|
||||
"--passfile2", $passfile2->filename,
|
||||
($dry eq "1" ? ('--dry') : ()),
|
||||
'--no-modulesversion',
|
||||
'--noreleasecheck'];
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@ LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
ARG APCU_PECL_VERSION=5.1.22
|
||||
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||
ARG IMAGICK_PECL_VERSION=3.7.0
|
||||
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
ARG MAILPARSE_PECL_VERSION=3.1.6
|
||||
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
ARG MEMCACHED_PECL_VERSION=3.2.0
|
||||
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||
ARG REDIS_PECL_VERSION=6.0.1
|
||||
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||
ARG COMPOSER_VERSION=2.6.5
|
||||
|
||||
RUN apk add -U --no-cache autoconf \
|
||||
|
||||
@@ -79,6 +79,9 @@ EOF
|
||||
redis-cli -h redis-mailcow SLAVEOF NO ONE
|
||||
fi
|
||||
|
||||
# Provide additional lua modules
|
||||
ln -s /usr/lib/$(uname -m)-linux-gnu/liblua5.1-cjson.so.0.0.0 /usr/lib/rspamd/cjson.so
|
||||
|
||||
chown -R _rspamd:_rspamd /var/lib/rspamd \
|
||||
/etc/rspamd/local.d \
|
||||
/etc/rspamd/override.d \
|
||||
|
||||
@@ -3,7 +3,7 @@ LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG SOGO_DEBIAN_REPOSITORY=http://packages.sogo.nu/nightly/5/debian/
|
||||
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
|
||||
ARG GOSU_VERSION=1.16
|
||||
ENV LC_ALL C
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ FROM solr:7.7-slim
|
||||
|
||||
USER root
|
||||
|
||||
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||
ARG GOSU_VERSION=1.16
|
||||
|
||||
COPY solr.sh /
|
||||
|
||||
@@ -86,7 +86,7 @@ server {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
|
||||
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+)\.php(?:$|\/) {
|
||||
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
|
||||
set $path_info $fastcgi_path_info;
|
||||
try_files $fastcgi_script_name =404;
|
||||
@@ -105,7 +105,7 @@ server {
|
||||
fastcgi_read_timeout 1200;
|
||||
}
|
||||
|
||||
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
|
||||
location ~ ^\/(?:updater|ocs-provider)(?:$|\/) {
|
||||
try_files $uri/ =404;
|
||||
index index.php;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Whitelist generated by Postwhite v3.4 on Sun Oct 1 00:14:59 UTC 2023
|
||||
# Whitelist generated by Postwhite v3.4 on Fri Dec 1 00:15:18 UTC 2023
|
||||
# https://github.com/stevejenkins/postwhite/
|
||||
# 2019 total rules
|
||||
# 2038 total rules
|
||||
2a00:1450:4000::/36 permit
|
||||
2a01:111:f400::/48 permit
|
||||
2a01:111:f403:8000::/50 permit
|
||||
@@ -10,10 +10,10 @@
|
||||
2a02:a60:0:5::/64 permit
|
||||
2c0f:fb50:4000::/36 permit
|
||||
2.207.151.53 permit
|
||||
3.14.230.16 permit
|
||||
3.70.123.177 permit
|
||||
3.93.157.0/24 permit
|
||||
3.129.120.190 permit
|
||||
3.137.78.75 permit
|
||||
3.210.190.0/24 permit
|
||||
8.20.114.31 permit
|
||||
8.25.194.0/23 permit
|
||||
@@ -183,8 +183,6 @@
|
||||
50.18.125.237 permit
|
||||
50.18.126.162 permit
|
||||
50.31.32.0/19 permit
|
||||
50.31.156.96/27 permit
|
||||
50.31.205.0/24 permit
|
||||
51.137.58.21 permit
|
||||
51.140.75.55 permit
|
||||
51.144.100.179 permit
|
||||
@@ -211,6 +209,7 @@
|
||||
52.96.222.194 permit
|
||||
52.96.222.226 permit
|
||||
52.96.223.2 permit
|
||||
52.96.228.130 permit
|
||||
52.96.229.242 permit
|
||||
52.100.0.0/14 permit
|
||||
52.103.0.0/17 permit
|
||||
@@ -303,22 +302,31 @@
|
||||
64.147.123.27 permit
|
||||
64.147.123.28 permit
|
||||
64.147.123.29 permit
|
||||
64.147.123.128/27 permit
|
||||
64.207.219.7 permit
|
||||
64.207.219.8 permit
|
||||
64.207.219.9 permit
|
||||
64.207.219.10 permit
|
||||
64.207.219.11 permit
|
||||
64.207.219.12 permit
|
||||
64.207.219.13 permit
|
||||
64.207.219.14 permit
|
||||
64.207.219.15 permit
|
||||
64.207.219.71 permit
|
||||
64.207.219.72 permit
|
||||
64.207.219.73 permit
|
||||
64.207.219.74 permit
|
||||
64.207.219.75 permit
|
||||
64.207.219.76 permit
|
||||
64.207.219.77 permit
|
||||
64.207.219.78 permit
|
||||
64.207.219.79 permit
|
||||
64.207.219.135 permit
|
||||
64.207.219.136 permit
|
||||
64.207.219.137 permit
|
||||
64.207.219.138 permit
|
||||
64.207.219.139 permit
|
||||
64.207.219.140 permit
|
||||
64.207.219.141 permit
|
||||
64.207.219.142 permit
|
||||
64.207.219.143 permit
|
||||
@@ -396,7 +404,6 @@
|
||||
66.196.81.232/31 permit
|
||||
66.196.81.234 permit
|
||||
66.211.168.230/31 permit
|
||||
66.211.170.86/31 permit
|
||||
66.211.170.88/29 permit
|
||||
66.211.184.0/23 permit
|
||||
66.218.74.64/30 permit
|
||||
@@ -620,7 +627,9 @@
|
||||
82.165.229.130 permit
|
||||
82.165.230.21 permit
|
||||
82.165.230.22 permit
|
||||
84.116.6.0/23 permit
|
||||
84.116.36.0/24 permit
|
||||
84.116.50.0/23 permit
|
||||
85.158.136.0/21 permit
|
||||
86.61.88.25 permit
|
||||
87.198.219.130 permit
|
||||
@@ -1192,7 +1201,6 @@
|
||||
104.130.96.0/28 permit
|
||||
104.130.122.0/23 permit
|
||||
104.214.25.77 permit
|
||||
104.245.209.192/26 permit
|
||||
106.10.144.64/27 permit
|
||||
106.10.144.100/31 permit
|
||||
106.10.144.103 permit
|
||||
@@ -1369,6 +1377,8 @@
|
||||
128.245.0.0/20 permit
|
||||
128.245.64.0/20 permit
|
||||
128.245.176.0/20 permit
|
||||
128.245.240.0/24 permit
|
||||
128.245.241.0/24 permit
|
||||
128.245.242.0/24 permit
|
||||
128.245.242.16 permit
|
||||
128.245.242.17 permit
|
||||
@@ -1378,6 +1388,7 @@
|
||||
128.245.245.0/24 permit
|
||||
128.245.246.0/24 permit
|
||||
128.245.247.0/24 permit
|
||||
128.245.248.0/21 permit
|
||||
129.41.77.70 permit
|
||||
129.41.169.249 permit
|
||||
129.80.5.164 permit
|
||||
@@ -1418,6 +1429,7 @@
|
||||
136.143.182.0/23 permit
|
||||
136.143.184.0/24 permit
|
||||
136.143.188.0/24 permit
|
||||
136.143.190.0/23 permit
|
||||
136.147.128.0/20 permit
|
||||
136.147.135.0/24 permit
|
||||
136.147.176.0/20 permit
|
||||
@@ -1454,7 +1466,6 @@
|
||||
146.20.215.0/24 permit
|
||||
146.20.215.182 permit
|
||||
146.88.28.0/24 permit
|
||||
147.160.158.0/24 permit
|
||||
147.243.1.47 permit
|
||||
147.243.1.48 permit
|
||||
147.243.1.153 permit
|
||||
@@ -1492,6 +1503,8 @@
|
||||
158.101.211.207 permit
|
||||
158.120.80.0/21 permit
|
||||
158.247.16.0/20 permit
|
||||
159.92.154.0/24 permit
|
||||
159.92.155.0/24 permit
|
||||
159.92.157.0/24 permit
|
||||
159.92.157.16 permit
|
||||
159.92.157.17 permit
|
||||
@@ -1501,6 +1514,9 @@
|
||||
159.92.160.0/24 permit
|
||||
159.92.161.0/24 permit
|
||||
159.92.162.0/24 permit
|
||||
159.92.163.0/24 permit
|
||||
159.92.164.0/22 permit
|
||||
159.92.168.0/21 permit
|
||||
159.112.240.0/20 permit
|
||||
159.112.242.162 permit
|
||||
159.135.132.128/25 permit
|
||||
@@ -1549,6 +1565,8 @@
|
||||
168.245.127.231 permit
|
||||
169.148.129.0/24 permit
|
||||
169.148.131.0/24 permit
|
||||
169.148.142.10 permit
|
||||
169.148.144.0/25 permit
|
||||
170.10.68.0/22 permit
|
||||
170.10.128.0/24 permit
|
||||
170.10.129.0/24 permit
|
||||
@@ -1700,13 +1718,14 @@
|
||||
198.244.60.0/22 permit
|
||||
198.245.80.0/20 permit
|
||||
198.245.81.0/24 permit
|
||||
199.15.176.173 permit
|
||||
199.15.213.187 permit
|
||||
199.15.226.37 permit
|
||||
199.16.156.0/22 permit
|
||||
199.33.145.1 permit
|
||||
199.33.145.32 permit
|
||||
199.34.22.36 permit
|
||||
199.59.148.0/22 permit
|
||||
199.67.80.2 permit
|
||||
199.67.84.0/24 permit
|
||||
199.67.86.0/24 permit
|
||||
199.67.88.0/24 permit
|
||||
|
||||
91
data/conf/rspamd/dynmaps/footer.php
Normal file
91
data/conf/rspamd/dynmaps/footer.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
// File size is limited by Nginx site to 10M
|
||||
// To speed things up, we do not include prerequisites
|
||||
header('Content-Type: text/plain');
|
||||
require_once "vars.inc.php";
|
||||
// Do not show errors, we log to using error_log
|
||||
ini_set('error_reporting', 0);
|
||||
// Init database
|
||||
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
|
||||
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
|
||||
$opt = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
try {
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
error_log("FOOTER: " . $e . PHP_EOL);
|
||||
http_response_code(501);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!function_exists('getallheaders')) {
|
||||
function getallheaders() {
|
||||
if (!is_array($_SERVER)) {
|
||||
return array();
|
||||
}
|
||||
$headers = array();
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
if (substr($name, 0, 5) == 'HTTP_') {
|
||||
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
|
||||
// Read headers
|
||||
$headers = getallheaders();
|
||||
// Get Domain
|
||||
$domain = $headers['Domain'];
|
||||
// Get Username
|
||||
$username = $headers['Username'];
|
||||
// Get From
|
||||
$from = $headers['From'];
|
||||
// define empty footer
|
||||
$empty_footer = json_encode(array(
|
||||
'html' => '',
|
||||
'plain' => '',
|
||||
'vars' => array()
|
||||
));
|
||||
|
||||
error_log("FOOTER: checking for domain " . $domain . ", user " . $username . " and address " . $from . PHP_EOL);
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `plain`, `html`, `mbox_exclude` FROM `domain_wide_footer`
|
||||
WHERE `domain` = :domain");
|
||||
$stmt->execute(array(
|
||||
':domain' => $domain
|
||||
));
|
||||
$footer = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (in_array($from, json_decode($footer['mbox_exclude']))){
|
||||
$footer = false;
|
||||
}
|
||||
if (empty($footer)){
|
||||
echo $empty_footer;
|
||||
exit;
|
||||
}
|
||||
error_log("FOOTER: " . json_encode($footer) . PHP_EOL);
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `custom_attributes` FROM `mailbox` WHERE `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
$custom_attributes = $stmt->fetch(PDO::FETCH_ASSOC)['custom_attributes'];
|
||||
if (empty($custom_attributes)){
|
||||
$custom_attributes = (object)array();
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
error_log("FOOTER: " . $e->getMessage() . PHP_EOL);
|
||||
http_response_code(502);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
// return footer
|
||||
$footer["vars"] = $custom_attributes;
|
||||
echo json_encode($footer);
|
||||
9
data/conf/rspamd/local.d/ratelimit.conf
Normal file
9
data/conf/rspamd/local.d/ratelimit.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
# Uncomment below to apply the ratelimits globally. Use Ratelimits inside mailcow UI to overwrite them for a specific domain/mailbox.
|
||||
# rates {
|
||||
# # Format: "1 / 1h" or "20 / 1m" etc.
|
||||
# to = "100 / 1s";
|
||||
# to_ip = "100 / 1s";
|
||||
# to_ip_from = "100 / 1s";
|
||||
# bounce_to = "100 / 1h";
|
||||
# bounce_to_ip = "7 / 1m";
|
||||
# }
|
||||
@@ -527,20 +527,21 @@ rspamd_config:register_symbol({
|
||||
name = 'MOO_FOOTER',
|
||||
type = 'prefilter',
|
||||
callback = function(task)
|
||||
local cjson = require "cjson"
|
||||
local lua_mime = require "lua_mime"
|
||||
local lua_util = require "lua_util"
|
||||
local rspamd_logger = require "rspamd_logger"
|
||||
local rspamd_redis = require "rspamd_redis"
|
||||
local ucl = require "ucl"
|
||||
local redis_params = rspamd_parse_redis_server('footer')
|
||||
local rspamd_http = require "rspamd_http"
|
||||
local envfrom = task:get_from(1)
|
||||
local uname = task:get_user()
|
||||
if not envfrom or not uname then
|
||||
return false
|
||||
end
|
||||
local uname = uname:lower()
|
||||
local env_from_domain = envfrom[1].domain:lower() -- get smtp from domain in lower case
|
||||
local env_from_domain = envfrom[1].domain:lower()
|
||||
local env_from_addr = envfrom[1].addr:lower()
|
||||
|
||||
-- determine newline type
|
||||
local function newline(task)
|
||||
local t = task:get_newlines_type()
|
||||
|
||||
@@ -552,20 +553,19 @@ rspamd_config:register_symbol({
|
||||
|
||||
return '\r\n'
|
||||
end
|
||||
local function redis_cb_footer(err, data)
|
||||
-- retrieve footer
|
||||
local function footer_cb(err_message, code, data, headers)
|
||||
if err or type(data) ~= 'string' then
|
||||
rspamd_logger.infox(rspamd_config, "domain wide footer request for user %s returned invalid or empty data (\"%s\") or error (\"%s\")", uname, data, err)
|
||||
else
|
||||
|
||||
-- parse json string
|
||||
local parser = ucl.parser()
|
||||
local res,err = parser:parse_string(data)
|
||||
if not res then
|
||||
local footer = cjson.decode(data)
|
||||
if not footer then
|
||||
rspamd_logger.infox(rspamd_config, "parsing domain wide footer for user %s returned invalid or empty data (\"%s\") or error (\"%s\")", uname, data, err)
|
||||
else
|
||||
local footer = parser:get_object()
|
||||
|
||||
if footer and type(footer) == "table" and (footer.html or footer.plain) then
|
||||
rspamd_logger.infox(rspamd_config, "found domain wide footer for user %s: html=%s, plain=%s", uname, footer.html, footer.plain)
|
||||
if footer and type(footer) == "table" and (footer.html and footer.html ~= "" or footer.plain and footer.plain ~= "") then
|
||||
rspamd_logger.infox(rspamd_config, "found domain wide footer for user %s: html=%s, plain=%s, vars=%s", uname, footer.html, footer.plain, footer.vars)
|
||||
|
||||
local envfrom_mime = task:get_from(2)
|
||||
local from_name = ""
|
||||
@@ -575,6 +575,7 @@ rspamd_config:register_symbol({
|
||||
from_name = envfrom[1].name
|
||||
end
|
||||
|
||||
-- default replacements
|
||||
local replacements = {
|
||||
auth_user = uname,
|
||||
from_user = envfrom[1].user,
|
||||
@@ -582,10 +583,20 @@ rspamd_config:register_symbol({
|
||||
from_addr = envfrom[1].addr,
|
||||
from_domain = envfrom[1].domain:lower()
|
||||
}
|
||||
if footer.html then
|
||||
-- add custom mailbox attributes
|
||||
if footer.vars and type(footer.vars) == "string" then
|
||||
local footer_vars = cjson.decode(footer.vars)
|
||||
|
||||
if type(footer_vars) == "table" then
|
||||
for key, value in pairs(footer_vars) do
|
||||
replacements[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
if footer.html and footer.html ~= "" then
|
||||
footer.html = lua_util.jinja_template(footer.html, replacements, true)
|
||||
end
|
||||
if footer.plain then
|
||||
if footer.plain and footer.plain ~= "" then
|
||||
footer.plain = lua_util.jinja_template(footer.plain, replacements, true)
|
||||
end
|
||||
|
||||
@@ -631,15 +642,19 @@ rspamd_config:register_symbol({
|
||||
end
|
||||
local out_parts = {}
|
||||
for _,o in ipairs(out) do
|
||||
if type(o) ~= 'table' then
|
||||
out_parts[#out_parts + 1] = o
|
||||
out_parts[#out_parts + 1] = newline_s
|
||||
else
|
||||
out_parts[#out_parts + 1] = o[1]
|
||||
if o[2] then
|
||||
out_parts[#out_parts + 1] = newline_s
|
||||
end
|
||||
end
|
||||
if type(o) ~= 'table' then
|
||||
out_parts[#out_parts + 1] = o
|
||||
out_parts[#out_parts + 1] = newline_s
|
||||
else
|
||||
local removePrefix = "--\x0D\x0AContent-Type"
|
||||
if string.lower(string.sub(tostring(o[1]), 1, string.len(removePrefix))) == string.lower(removePrefix) then
|
||||
o[1] = string.sub(tostring(o[1]), string.len("--\x0D\x0A") + 1)
|
||||
end
|
||||
out_parts[#out_parts + 1] = o[1]
|
||||
if o[2] then
|
||||
out_parts[#out_parts + 1] = newline_s
|
||||
end
|
||||
end
|
||||
end
|
||||
task:set_message(out_parts)
|
||||
else
|
||||
@@ -649,17 +664,14 @@ rspamd_config:register_symbol({
|
||||
end
|
||||
end
|
||||
|
||||
local redis_ret_footer = rspamd_redis_make_request(task,
|
||||
redis_params, -- connect params
|
||||
env_from_domain, -- hash key
|
||||
false, -- is write
|
||||
redis_cb_footer, --callback
|
||||
'HGET', -- command
|
||||
{"DOMAIN_WIDE_FOOTER", env_from_domain} -- arguments
|
||||
)
|
||||
if not redis_ret_footer then
|
||||
rspamd_logger.infox(rspamd_config, "cannot make request to load footer for domain")
|
||||
end
|
||||
-- fetch footer
|
||||
rspamd_http.request({
|
||||
task=task,
|
||||
url='http://nginx:8081/footer.php',
|
||||
body='',
|
||||
callback=footer_cb,
|
||||
headers={Domain=env_from_domain,Username=uname,From=env_from_addr},
|
||||
})
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
rates {
|
||||
# Format: "1 / 1h" or "20 / 1m" etc. - global ratelimits are disabled by default
|
||||
to = "100 / 1s";
|
||||
to_ip = "100 / 1s";
|
||||
to_ip_from = "100 / 1s";
|
||||
bounce_to = "100 / 1h";
|
||||
bounce_to_ip = "7 / 1m";
|
||||
}
|
||||
whitelisted_rcpts = "postmaster,mailer-daemon";
|
||||
max_rcpt = 25;
|
||||
custom_keywords = "/etc/rspamd/lua/ratelimit.lua";
|
||||
|
||||
@@ -3137,6 +3137,86 @@ paths:
|
||||
type: string
|
||||
type: object
|
||||
summary: Update domain
|
||||
/api/v1/edit/domain/footer:
|
||||
post:
|
||||
responses:
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
- log:
|
||||
- mailbox
|
||||
- edit
|
||||
- domain_wide_footer
|
||||
- domains:
|
||||
- mailcow.tld
|
||||
html: "<br>foo {= foo =}"
|
||||
plain: "<foo {= foo =}"
|
||||
mbox_exclude:
|
||||
- moo@mailcow.tld
|
||||
- null
|
||||
msg:
|
||||
- domain_footer_modified
|
||||
- mailcow.tld
|
||||
type: success
|
||||
schema:
|
||||
properties:
|
||||
log:
|
||||
description: contains request object
|
||||
items: {}
|
||||
type: array
|
||||
msg:
|
||||
items: {}
|
||||
type: array
|
||||
type:
|
||||
enum:
|
||||
- success
|
||||
- danger
|
||||
- error
|
||||
type: string
|
||||
type: object
|
||||
description: OK
|
||||
headers: {}
|
||||
tags:
|
||||
- Domains
|
||||
description: >-
|
||||
You can update the footer of one or more domains per request.
|
||||
operationId: Update domain wide footer
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
example:
|
||||
attr:
|
||||
html: "<br>foo {= foo =}"
|
||||
plain: "foo {= foo =}"
|
||||
mbox_exclude:
|
||||
- moo@mailcow.tld
|
||||
items: mailcow.tld
|
||||
properties:
|
||||
attr:
|
||||
properties:
|
||||
html:
|
||||
description: Footer text in HTML format
|
||||
type: string
|
||||
plain:
|
||||
description: Footer text in PLAIN text format
|
||||
type: string
|
||||
mbox_exclude:
|
||||
description: Array of mailboxes to exclude from domain wide footer
|
||||
type: object
|
||||
type: object
|
||||
items:
|
||||
description: contains a list of domain names where you want to update the footer
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: object
|
||||
summary: Update domain wide footer
|
||||
/api/v1/edit/fail2ban:
|
||||
post:
|
||||
responses:
|
||||
@@ -3336,6 +3416,86 @@ paths:
|
||||
type: object
|
||||
type: object
|
||||
summary: Update mailbox
|
||||
/api/v1/edit/mailbox/custom-attribute:
|
||||
post:
|
||||
responses:
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
- log:
|
||||
- mailbox
|
||||
- edit
|
||||
- mailbox_custom_attribute
|
||||
- mailboxes:
|
||||
- moo@mailcow.tld
|
||||
attribute:
|
||||
- role
|
||||
- foo
|
||||
value:
|
||||
- cow
|
||||
- bar
|
||||
- null
|
||||
msg:
|
||||
- mailbox_modified
|
||||
- moo@mailcow.tld
|
||||
type: success
|
||||
schema:
|
||||
properties:
|
||||
log:
|
||||
description: contains request object
|
||||
items: {}
|
||||
type: array
|
||||
msg:
|
||||
items: {}
|
||||
type: array
|
||||
type:
|
||||
enum:
|
||||
- success
|
||||
- danger
|
||||
- error
|
||||
type: string
|
||||
type: object
|
||||
description: OK
|
||||
headers: {}
|
||||
tags:
|
||||
- Mailboxes
|
||||
description: >-
|
||||
You can update custom attributes of one or more mailboxes per request.
|
||||
operationId: Update mailbox custom attributes
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
example:
|
||||
attr:
|
||||
attribute:
|
||||
- role
|
||||
- foo
|
||||
value:
|
||||
- cow
|
||||
- bar
|
||||
items:
|
||||
- moo@mailcow.tld
|
||||
properties:
|
||||
attr:
|
||||
properties:
|
||||
attribute:
|
||||
description: Array of attribute keys
|
||||
type: object
|
||||
value:
|
||||
description: Array of attribute values
|
||||
type: object
|
||||
type: object
|
||||
items:
|
||||
description: contains list of mailboxes you want update
|
||||
type: object
|
||||
type: object
|
||||
summary: Update mailbox custom attributes
|
||||
/api/v1/edit/mailq:
|
||||
post:
|
||||
responses:
|
||||
@@ -5581,6 +5741,7 @@ paths:
|
||||
sogo_access: "1"
|
||||
tls_enforce_in: "0"
|
||||
tls_enforce_out: "0"
|
||||
custom_attributes: {}
|
||||
domain: domain3.tld
|
||||
is_relayed: 0
|
||||
local_part: info
|
||||
@@ -5646,6 +5807,40 @@ paths:
|
||||
items:
|
||||
type: string
|
||||
summary: Edit Cross-Origin Resource Sharing (CORS) settings
|
||||
"/api/v1/get/spam-score/{mailbox}":
|
||||
get:
|
||||
parameters:
|
||||
- description: name of mailbox or empty for current user - admin user will retrieve the global spam filter score
|
||||
in: path
|
||||
name: mailbox
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: e.g. api-key-string
|
||||
example: api-key-string
|
||||
in: header
|
||||
name: X-API-Key
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
examples:
|
||||
response:
|
||||
value:
|
||||
spam_score: "8,15"
|
||||
description: OK
|
||||
headers: {}
|
||||
tags:
|
||||
- Mailboxes
|
||||
description: >-
|
||||
Using this endpoint you can get the global spam filter score or the spam filter score of a certain mailbox.
|
||||
operationId: Get mailbox or global spam filter score
|
||||
summary: Get mailbox or global spam filter score
|
||||
|
||||
tags:
|
||||
- name: Domains
|
||||
|
||||
@@ -58,6 +58,8 @@ if (isset($_SESSION['mailcow_cc_role'])) {
|
||||
'dkim' => dkim('details', $domain),
|
||||
'domain_details' => $result,
|
||||
'domain_footer' => $domain_footer,
|
||||
'mailboxes' => mailbox('get', 'mailboxes', $_GET["domain"]),
|
||||
'aliases' => mailbox('get', 'aliases', $_GET["domain"], 'address')
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -218,6 +220,7 @@ $js_minifier->add('/web/js/site/pwgen.js');
|
||||
$template_data['result'] = $result;
|
||||
$template_data['return_to'] = $_SESSION['return_to'];
|
||||
$template_data['lang_user'] = json_encode($lang['user']);
|
||||
$template_data['lang_admin'] = json_encode($lang['admin']);
|
||||
$template_data['lang_datatables'] = json_encode($lang['datatables']);
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
|
||||
|
||||
@@ -325,6 +325,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$timeout2 = intval($_data['timeout2']);
|
||||
$skipcrossduplicates = intval($_data['skipcrossduplicates']);
|
||||
$automap = intval($_data['automap']);
|
||||
$dry = intval($_data['dry']);
|
||||
$port1 = $_data['port1'];
|
||||
$host1 = strtolower($_data['host1']);
|
||||
$password1 = $_data['password1'];
|
||||
@@ -435,8 +436,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `timeout1`, `timeout2`, `automap`, `skipcrossduplicates`, `maxbytespersecond`, `subscribeall`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `custom_params`, `active`)
|
||||
VALUES (:user2, :exclude, :delete1, :delete2, :timeout1, :timeout2, :automap, :skipcrossduplicates, :maxbytespersecond, :subscribeall, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :custom_params, :active)");
|
||||
$stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `timeout1`, `timeout2`, `automap`, `skipcrossduplicates`, `maxbytespersecond`, `subscribeall`, `dry`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `custom_params`, `active`)
|
||||
VALUES (:user2, :exclude, :delete1, :delete2, :timeout1, :timeout2, :automap, :skipcrossduplicates, :maxbytespersecond, :subscribeall, :dry, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :custom_params, :active)");
|
||||
$stmt->execute(array(
|
||||
':user2' => $username,
|
||||
':custom_params' => $custom_params,
|
||||
@@ -450,6 +451,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':skipcrossduplicates' => $skipcrossduplicates,
|
||||
':maxbytespersecond' => $maxbytespersecond,
|
||||
':subscribeall' => $subscribeall,
|
||||
':dry' => $dry,
|
||||
':subfolder2' => $subfolder2,
|
||||
':host1' => $host1,
|
||||
':authmech1' => 'PLAIN',
|
||||
@@ -2031,6 +2033,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$success = (isset($_data['success'])) ? NULL : $is_now['success'];
|
||||
$delete2duplicates = (isset($_data['delete2duplicates'])) ? intval($_data['delete2duplicates']) : $is_now['delete2duplicates'];
|
||||
$subscribeall = (isset($_data['subscribeall'])) ? intval($_data['subscribeall']) : $is_now['subscribeall'];
|
||||
$dry = (isset($_data['dry'])) ? intval($_data['dry']) : $is_now['dry'];
|
||||
$delete1 = (isset($_data['delete1'])) ? intval($_data['delete1']) : $is_now['delete1'];
|
||||
$delete2 = (isset($_data['delete2'])) ? intval($_data['delete2']) : $is_now['delete2'];
|
||||
$automap = (isset($_data['automap'])) ? intval($_data['automap']) : $is_now['automap'];
|
||||
@@ -2164,6 +2167,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
`timeout1` = :timeout1,
|
||||
`timeout2` = :timeout2,
|
||||
`subscribeall` = :subscribeall,
|
||||
`dry` = :dry,
|
||||
`active` = :active
|
||||
WHERE `id` = :id");
|
||||
$stmt->execute(array(
|
||||
@@ -2189,6 +2193,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
':timeout1' => $timeout1,
|
||||
':timeout2' => $timeout2,
|
||||
':subscribeall' => $subscribeall,
|
||||
':dry' => $dry,
|
||||
':active' => $active,
|
||||
));
|
||||
$_SESSION['return'][] = array(
|
||||
@@ -3259,6 +3264,62 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
case 'mailbox_custom_attribute':
|
||||
$_data['attribute'] = isset($_data['attribute']) ? $_data['attribute'] : array();
|
||||
$_data['attribute'] = is_array($_data['attribute']) ? $_data['attribute'] : array($_data['attribute']);
|
||||
$_data['attribute'] = array_map(function($value) { return str_replace(' ', '', $value); }, $_data['attribute']);
|
||||
$_data['value'] = isset($_data['value']) ? $_data['value'] : array();
|
||||
$_data['value'] = is_array($_data['value']) ? $_data['value'] : array($_data['value']);
|
||||
$attributes = (object)array_combine($_data['attribute'], $_data['value']);
|
||||
$mailboxes = is_array($_data['mailboxes']) ? $_data['mailboxes'] : array($_data['mailboxes']);
|
||||
|
||||
foreach ($mailboxes as $mailbox) {
|
||||
if (!filter_var($mailbox, FILTER_VALIDATE_EMAIL)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('username_invalid', $mailbox)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$is_now = mailbox('get', 'mailbox_details', $mailbox);
|
||||
if(!empty($is_now)){
|
||||
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $is_now['domain'])) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE `mailbox`
|
||||
SET `custom_attributes` = :custom_attributes
|
||||
WHERE username = :username");
|
||||
$stmt->execute(array(
|
||||
":username" => $mailbox,
|
||||
":custom_attributes" => json_encode($attributes)
|
||||
));
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('mailbox_modified', $mailbox)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
break;
|
||||
case 'resource':
|
||||
if (!is_array($_data['name'])) {
|
||||
$names = array();
|
||||
@@ -3338,44 +3399,89 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'domain_wide_footer':
|
||||
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
|
||||
if (!is_valid_domain_name($domain)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'domain_invalid'
|
||||
);
|
||||
return false;
|
||||
case 'domain_wide_footer':
|
||||
if (!is_array($_data['domains'])) {
|
||||
$domains = array();
|
||||
$domains[] = $_data['domains'];
|
||||
}
|
||||
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
else {
|
||||
$domains = $_data['domains'];
|
||||
}
|
||||
|
||||
$footers = array();
|
||||
$footers['html'] = isset($_data['footer_html']) ? $_data['footer_html'] : '';
|
||||
$footers['plain'] = isset($_data['footer_plain']) ? $_data['footer_plain'] : '';
|
||||
try {
|
||||
$redis->hSet('DOMAIN_WIDE_FOOTER', $domain, json_encode($footers));
|
||||
$footers['html'] = isset($_data['html']) ? $_data['html'] : '';
|
||||
$footers['plain'] = isset($_data['plain']) ? $_data['plain'] : '';
|
||||
$footers['mbox_exclude'] = array();
|
||||
if (isset($_data["mbox_exclude"])){
|
||||
if (!is_array($_data["mbox_exclude"])) {
|
||||
$_data["mbox_exclude"] = array($_data["mbox_exclude"]);
|
||||
}
|
||||
foreach ($_data["mbox_exclude"] as $mailbox) {
|
||||
if (!filter_var($mailbox, FILTER_VALIDATE_EMAIL)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('username_invalid', $mailbox)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
$is_now = mailbox('get', 'mailbox_details', $mailbox);
|
||||
if(empty($is_now)){
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('username_invalid', $mailbox)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
array_push($footers['mbox_exclude'], $mailbox);
|
||||
}
|
||||
}
|
||||
catch (RedisException $e) {
|
||||
foreach ($domains as $domain) {
|
||||
$domain = idn_to_ascii(strtolower(trim($domain)), 0, INTL_IDNA_VARIANT_UTS46);
|
||||
if (!is_valid_domain_name($domain)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'domain_invalid'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("DELETE FROM `domain_wide_footer` WHERE `domain`= :domain");
|
||||
$stmt->execute(array(':domain' => $domain));
|
||||
$stmt = $pdo->prepare("INSERT INTO `domain_wide_footer` (`domain`, `html`, `plain`, `mbox_exclude`) VALUES (:domain, :html, :plain, :mbox_exclude)");
|
||||
$stmt->execute(array(
|
||||
':domain' => $domain,
|
||||
':html' => $footers['html'],
|
||||
':plain' => $footers['plain'],
|
||||
':mbox_exclude' => json_encode($footers['mbox_exclude']),
|
||||
));
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => $e->getMessage()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('redis_error', $e)
|
||||
'msg' => array('domain_footer_modified', htmlspecialchars($domain))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('domain_footer_modified', htmlspecialchars($domain))
|
||||
);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -3929,13 +4035,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `id` FROM `alias` WHERE `address` != `goto` AND `domain` = :domain");
|
||||
$stmt = $pdo->prepare("SELECT `id`, `address` FROM `alias` WHERE `address` != `goto` AND `domain` = :domain");
|
||||
$stmt->execute(array(
|
||||
':domain' => $_data,
|
||||
));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($row = array_shift($rows)) {
|
||||
$aliases[] = $row['id'];
|
||||
if ($_extra == "address"){
|
||||
$aliases[] = $row['address'];
|
||||
} else {
|
||||
$aliases[] = $row['id'];
|
||||
}
|
||||
}
|
||||
return $aliases;
|
||||
break;
|
||||
@@ -4287,6 +4397,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
`mailbox`.`modified`,
|
||||
`quota2`.`bytes`,
|
||||
`attributes`,
|
||||
`custom_attributes`,
|
||||
`quota2`.`messages`
|
||||
FROM `mailbox`, `quota2`, `domain`
|
||||
WHERE (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)
|
||||
@@ -4307,6 +4418,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
`mailbox`.`modified`,
|
||||
`quota2replica`.`bytes`,
|
||||
`attributes`,
|
||||
`custom_attributes`,
|
||||
`quota2replica`.`messages`
|
||||
FROM `mailbox`, `quota2replica`, `domain`
|
||||
WHERE (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)
|
||||
@@ -4329,6 +4441,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$mailboxdata['quota'] = $row['quota'];
|
||||
$mailboxdata['messages'] = $row['messages'];
|
||||
$mailboxdata['attributes'] = json_decode($row['attributes'], true);
|
||||
$mailboxdata['custom_attributes'] = json_decode($row['custom_attributes'], true);
|
||||
$mailboxdata['quota_used'] = intval($row['bytes']);
|
||||
$mailboxdata['percent_in_use'] = ($row['quota'] == 0) ? '- ' : round((intval($row['bytes']) / intval($row['quota'])) * 100);
|
||||
$mailboxdata['created'] = $row['created'];
|
||||
@@ -4509,19 +4622,23 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
}
|
||||
|
||||
try {
|
||||
$footers = $redis->hGet('DOMAIN_WIDE_FOOTER', $domain);
|
||||
$footers = json_decode($footers, true);
|
||||
$stmt = $pdo->prepare("SELECT `html`, `plain`, `mbox_exclude` FROM `domain_wide_footer`
|
||||
WHERE `domain` = :domain");
|
||||
$stmt->execute(array(
|
||||
':domain' => $domain
|
||||
));
|
||||
$footer = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch (RedisException $e) {
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('redis_error', $e)
|
||||
'msg' => $e->getMessage()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $footers;
|
||||
return $footer;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3,7 +3,7 @@ function init_db_schema() {
|
||||
try {
|
||||
global $pdo;
|
||||
|
||||
$db_version = "14022023_1000";
|
||||
$db_version = "21112023_1644";
|
||||
|
||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
@@ -267,6 +267,20 @@ function init_db_schema() {
|
||||
),
|
||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||
),
|
||||
"domain_wide_footer" => array(
|
||||
"cols" => array(
|
||||
"domain" => "VARCHAR(255) NOT NULL",
|
||||
"html" => "LONGTEXT",
|
||||
"plain" => "LONGTEXT",
|
||||
"mbox_exclude" => "JSON NOT NULL DEFAULT ('[]')",
|
||||
),
|
||||
"keys" => array(
|
||||
"primary" => array(
|
||||
"" => array("domain")
|
||||
)
|
||||
),
|
||||
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
|
||||
),
|
||||
"tags_domain" => array(
|
||||
"cols" => array(
|
||||
"tag_name" => "VARCHAR(255) NOT NULL",
|
||||
@@ -344,6 +358,7 @@ function init_db_schema() {
|
||||
"local_part" => "VARCHAR(255) NOT NULL",
|
||||
"domain" => "VARCHAR(255) NOT NULL",
|
||||
"attributes" => "JSON",
|
||||
"custom_attributes" => "JSON NOT NULL DEFAULT ('{}')",
|
||||
"kind" => "VARCHAR(100) NOT NULL DEFAULT ''",
|
||||
"multiple_bookings" => "INT NOT NULL DEFAULT -1",
|
||||
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
|
||||
@@ -704,6 +719,7 @@ function init_db_schema() {
|
||||
"timeout1" => "SMALLINT NOT NULL DEFAULT '600'",
|
||||
"timeout2" => "SMALLINT NOT NULL DEFAULT '600'",
|
||||
"subscribeall" => "TINYINT(1) NOT NULL DEFAULT '1'",
|
||||
"dry" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
"is_running" => "TINYINT(1) NOT NULL DEFAULT '0'",
|
||||
"returned_text" => "LONGTEXT",
|
||||
"last_run" => "TIMESTAMP NULL DEFAULT NULL",
|
||||
|
||||
@@ -19,10 +19,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.composer/cache/files
|
||||
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
|
||||
@@ -52,10 +52,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.composer/cache/files
|
||||
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
dependency-version: [prefer-lowest, prefer-stable]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer update --no-progress --ignore-platform-reqs
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Install PHP with extensions"
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Install PHP with extensions"
|
||||
uses: shivammathur/setup-php@v2
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Set-up PHP"
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.composercache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Run DOCtor-RST"
|
||||
uses: docker://oskarstark/doctor-rst
|
||||
|
||||
18
data/web/inc/presets/sieve/sieve_8.yml
Normal file
18
data/web/inc/presets/sieve/sieve_8.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
headline: lang.sieve_preset_8
|
||||
content: |
|
||||
require "fileinto";
|
||||
require "mailbox";
|
||||
require "variables";
|
||||
require "subaddress";
|
||||
require "envelope";
|
||||
require "duplicate";
|
||||
require "imap4flags";
|
||||
if header :matches "To" "*mail@domain.tld*" {
|
||||
redirect "anothermail@anotherdomain.tld";
|
||||
setflag "\\seen"; /* Mark mail as read */
|
||||
fileInto "INBOX/SubFolder"; /* Move mail on subfolder after */
|
||||
} else {
|
||||
# The rest goes into INBOX
|
||||
# default is "implicit keep", we do it explicitly here
|
||||
keep;
|
||||
}
|
||||
@@ -97,6 +97,7 @@ $AVAILABLE_LANGUAGES = array(
|
||||
'lv-lv' => 'latviešu (Latvian)',
|
||||
'nl-nl' => 'Nederlands (Dutch)',
|
||||
'pl-pl' => 'Język Polski (Polish)',
|
||||
'pt-br' => 'Português brasileiro (Brazilian Portuguese)',
|
||||
'pt-pt' => 'Português (Portuguese)',
|
||||
'ro-ro' => 'Română (Romanian)',
|
||||
'ru-ru' => 'Pусский (Russian)',
|
||||
@@ -235,118 +236,120 @@ $RSPAMD_MAPS = array(
|
||||
|
||||
$IMAPSYNC_OPTIONS = array(
|
||||
'whitelist' => array(
|
||||
'abort',
|
||||
'authmd51',
|
||||
'authmd52',
|
||||
'authmech1',
|
||||
'authmech2',
|
||||
'authuser1',
|
||||
'authuser2',
|
||||
'debugcontent',
|
||||
'disarmreadreceipts',
|
||||
'logdir',
|
||||
'debugcrossduplicates',
|
||||
'maxsize',
|
||||
'minsize',
|
||||
'minage',
|
||||
'search',
|
||||
'noabletosearch',
|
||||
'pidfile',
|
||||
'pidfilelocking',
|
||||
'search1',
|
||||
'search2',
|
||||
'sslargs1',
|
||||
'sslargs2',
|
||||
'syncduplicates',
|
||||
'usecache',
|
||||
'synclabels',
|
||||
'truncmess',
|
||||
'domino2',
|
||||
'expunge1',
|
||||
'filterbuggyflags',
|
||||
'justconnect',
|
||||
'justfolders',
|
||||
'maxlinelength',
|
||||
'useheader',
|
||||
'noabletosearch1',
|
||||
'nolog',
|
||||
'prefix1',
|
||||
'prefix2',
|
||||
'sep1',
|
||||
'sep2',
|
||||
'nofoldersizesatend',
|
||||
'justfoldersizes',
|
||||
'proxyauth1',
|
||||
'skipemptyfolders',
|
||||
'include',
|
||||
'subfolder1',
|
||||
'subscribed',
|
||||
'subscribe',
|
||||
'debug',
|
||||
'debugcontent',
|
||||
'debugcrossduplicates',
|
||||
'debugflags',
|
||||
'debugfolders',
|
||||
'debugimap',
|
||||
'debugimap1',
|
||||
'debugimap2',
|
||||
'debugmemory',
|
||||
'debugssl',
|
||||
'delete1emptyfolders',
|
||||
'delete2folders',
|
||||
'disarmreadreceipts',
|
||||
'domain1',
|
||||
'domain2',
|
||||
'domino1',
|
||||
'domino2',
|
||||
'dry',
|
||||
'errorsmax',
|
||||
'exchange1',
|
||||
'exchange2',
|
||||
'exitwhenover',
|
||||
'expunge1',
|
||||
'f1f2',
|
||||
'filterbuggyflags',
|
||||
'folder',
|
||||
'folderfirst',
|
||||
'folderlast',
|
||||
'folderrec',
|
||||
'gmail1',
|
||||
'gmail2',
|
||||
'idatefromheader',
|
||||
'include',
|
||||
'inet4',
|
||||
'inet6',
|
||||
'justconnect',
|
||||
'justfolders',
|
||||
'justfoldersizes',
|
||||
'justlogin',
|
||||
'keepalive1',
|
||||
'keepalive2',
|
||||
'log',
|
||||
'logdir',
|
||||
'logfile',
|
||||
'maxbytesafter',
|
||||
'maxlinelength',
|
||||
'maxmessagespersecond',
|
||||
'maxsize',
|
||||
'maxsleep',
|
||||
'minage',
|
||||
'minsize',
|
||||
'noabletosearch',
|
||||
'noabletosearch1',
|
||||
'noabletosearch2',
|
||||
'noexpunge1',
|
||||
'noexpunge2',
|
||||
'nofoldersizesatend',
|
||||
'noid',
|
||||
'nolog',
|
||||
'nomixfolders',
|
||||
'noresyncflags',
|
||||
'nossl1',
|
||||
'nouidexpunge2',
|
||||
'syncinternaldates',
|
||||
'idatefromheader',
|
||||
'useuid',
|
||||
'debugflags',
|
||||
'debugimap',
|
||||
'delete1emptyfolders',
|
||||
'delete2folders',
|
||||
'gmail2',
|
||||
'office1',
|
||||
'testslive6',
|
||||
'debugimap1',
|
||||
'errorsmax',
|
||||
'tests',
|
||||
'gmail1',
|
||||
'maxmessagespersecond',
|
||||
'maxbytesafter',
|
||||
'maxsleep',
|
||||
'abort',
|
||||
'resyncflags',
|
||||
'resynclabels',
|
||||
'syncacls',
|
||||
'nossl2',
|
||||
'nosyncacls',
|
||||
'notls1',
|
||||
'notls2',
|
||||
'nouidexpunge2',
|
||||
'nousecache',
|
||||
'office2',
|
||||
'testslive',
|
||||
'debugmemory',
|
||||
'exitwhenover',
|
||||
'noid',
|
||||
'noexpunge1',
|
||||
'authmd51',
|
||||
'logfile',
|
||||
'proxyauth2',
|
||||
'domain1',
|
||||
'domain2',
|
||||
'oauthaccesstoken1',
|
||||
'oauthaccesstoken2',
|
||||
'oauthdirect1',
|
||||
'oauthdirect2',
|
||||
'folder',
|
||||
'folderrec',
|
||||
'folderfirst',
|
||||
'folderlast',
|
||||
'nomixfolders',
|
||||
'authmd52',
|
||||
'debugfolders',
|
||||
'nossl2',
|
||||
'office1',
|
||||
'office2',
|
||||
'pidfile',
|
||||
'pidfilelocking',
|
||||
'prefix1',
|
||||
'prefix2',
|
||||
'proxyauth1',
|
||||
'proxyauth2',
|
||||
'resyncflags',
|
||||
'resynclabels',
|
||||
'search',
|
||||
'search1',
|
||||
'search2',
|
||||
'sep1',
|
||||
'sep2',
|
||||
'showpasswords',
|
||||
'skipemptyfolders',
|
||||
'ssl2',
|
||||
'sslargs1',
|
||||
'sslargs2',
|
||||
'subfolder1',
|
||||
'subscribe',
|
||||
'subscribed',
|
||||
'syncacls',
|
||||
'syncduplicates',
|
||||
'syncinternaldates',
|
||||
'synclabels',
|
||||
'tests',
|
||||
'testslive',
|
||||
'testslive6',
|
||||
'tls2',
|
||||
'notls2',
|
||||
'debugssl',
|
||||
'notls1',
|
||||
'inet4',
|
||||
'inet6',
|
||||
'log',
|
||||
'showpasswords'
|
||||
'truncmess',
|
||||
'usecache',
|
||||
'useheader',
|
||||
'useuid'
|
||||
),
|
||||
'blacklist' => array(
|
||||
'skipmess',
|
||||
|
||||
@@ -1684,7 +1684,7 @@ function showVersionModal(title, version){
|
||||
function parseGithubMarkdownLinks(inputText) {
|
||||
var replacedText, replacePattern1;
|
||||
|
||||
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) => {
|
||||
if (matched.includes('github.com')){
|
||||
// return short link if it's github link
|
||||
|
||||
@@ -199,6 +199,23 @@ jQuery(function($){
|
||||
});
|
||||
}
|
||||
|
||||
function add_table_row(table_id, type) {
|
||||
var row = $('<tr />');
|
||||
if (type == "mbox_attr") {
|
||||
cols = '<td><input class="input-sm input-xs-lg form-control" data-id="mbox_attr" type="text" name="attribute" required></td>';
|
||||
cols += '<td><input class="input-sm input-xs-lg form-control" data-id="mbox_attr" type="text" name="value" required></td>';
|
||||
cols += '<td><a href="#" role="button" class="btn btn-sm btn-xs-lg btn-secondary h-100 w-100" type="button">' + lang_admin.remove_row + '</a></td>';
|
||||
}
|
||||
row.append(cols);
|
||||
table_id.append(row);
|
||||
}
|
||||
$('#mbox_attr_table').on('click', 'tr a', function (e) {
|
||||
e.preventDefault();
|
||||
$(this).parents('tr').remove();
|
||||
});
|
||||
$('#add_mbox_attr_row').click(function() {
|
||||
add_table_row($('#mbox_attr_table'), "mbox_attr");
|
||||
});
|
||||
|
||||
// detect element visibility changes
|
||||
function onVisible(element, callback) {
|
||||
|
||||
@@ -220,7 +220,7 @@ jQuery(function($){
|
||||
if (value.score > 0) highlightClass = 'negative';
|
||||
else if (value.score < 0) highlightClass = 'positive';
|
||||
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 ? escapeHtml(value.options.join(', ')) : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
|
||||
});
|
||||
$('[data-bs-toggle="tooltip"]').tooltip();
|
||||
}
|
||||
@@ -295,3 +295,7 @@ jQuery(function($){
|
||||
$(".table_collapse_option").hide();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1591,6 +1591,12 @@ if (isset($_GET['query'])) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "spam-score":
|
||||
$score = mailbox('get', 'spam_score', $object);
|
||||
if ($score)
|
||||
$score = array("score" => preg_replace("/\s+/", "", $score));
|
||||
process_get_return($score);
|
||||
break;
|
||||
break;
|
||||
// return no route found if no case is matched
|
||||
default:
|
||||
@@ -1867,8 +1873,6 @@ if (isset($_GET['query'])) {
|
||||
case "quota_notification_bcc":
|
||||
process_edit_return(quota_notification_bcc('edit', $attr));
|
||||
break;
|
||||
case "domain-wide-footer":
|
||||
process_edit_return(mailbox('edit', 'domain_wide_footer', $attr));
|
||||
break;
|
||||
case "mailq":
|
||||
process_edit_return(mailq('edit', array_merge(array('qid' => $items), $attr)));
|
||||
@@ -1881,6 +1885,9 @@ if (isset($_GET['query'])) {
|
||||
case "template":
|
||||
process_edit_return(mailbox('edit', 'mailbox_templates', array_merge(array('ids' => $items), $attr)));
|
||||
break;
|
||||
case "custom-attribute":
|
||||
process_edit_return(mailbox('edit', 'mailbox_custom_attribute', array_merge(array('mailboxes' => $items), $attr)));
|
||||
break;
|
||||
default:
|
||||
process_edit_return(mailbox('edit', 'mailbox', array_merge(array('username' => $items), $attr)));
|
||||
break;
|
||||
@@ -1900,6 +1907,9 @@ if (isset($_GET['query'])) {
|
||||
case "template":
|
||||
process_edit_return(mailbox('edit', 'domain_templates', array_merge(array('ids' => $items), $attr)));
|
||||
break;
|
||||
case "footer":
|
||||
process_edit_return(mailbox('edit', 'domain_wide_footer', array_merge(array('domains' => $items), $attr)));
|
||||
break;
|
||||
default:
|
||||
process_edit_return(mailbox('edit', 'domain', array_merge(array('domain' => $items), $attr)));
|
||||
break;
|
||||
|
||||
@@ -147,6 +147,8 @@
|
||||
"ays": "Opravdu chcete pokračovat?",
|
||||
"ban_list_info": "Seznam blokovaných IP adres je zobrazen níže: <b>síť (zbývající čas blokování) - [akce]</b>.<br />IP adresy zařazené pro odblokování budou z aktivního seznamu odebrány během několika sekund.<br />Červeně označené položky jsou pernamentní bloky z blacklistu.",
|
||||
"change_logo": "Změnit logo",
|
||||
"logo_normal_label": "Normální",
|
||||
"logo_dark_label": "Inverzní pro tmavý režim",
|
||||
"configuration": "Nastavení",
|
||||
"convert_html_to_text": "Převést HTML do prostého textu",
|
||||
"credentials_transport_warning": "<b>Upozornění</b>: Přidání položky do transportní mapy aktualizuje také přihlašovací údaje všech záznamů s odpovídajícím skokem.",
|
||||
@@ -206,6 +208,9 @@
|
||||
"include_exclude": "Zahrnout/Vyloučit",
|
||||
"include_exclude_info": "Ve výchozím nastavení (bez výběru), jsou adresovány <b>všechny mailové schránky</b>",
|
||||
"includes": "Zahrnout tyto přijemce",
|
||||
"ip_check": "Kontrola IP",
|
||||
"ip_check_disabled": "Kontrola IP je zakázána. Můžete ji povolit v nabídce<br> <strong>Systém > Nastavení > Možnosti > Přizpůsobení</strong>",
|
||||
"ip_check_opt_in": "Přihlásit se k používání služby třetí strany <strong>ipv4.mailcow.email</strong> a <strong>ipv6.mailcow.email</strong> pro zjištění externích IP adres.",
|
||||
"is_mx_based": "Na základě MX",
|
||||
"last_applied": "Naposledy použité",
|
||||
"license_info": "Licence není povinná, pomůžete však dalšímu vývoji.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Registrujte si své GUID</a>, nebo si <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Support order\">zaplaťte podporu pro svou instalaci mailcow.</a>",
|
||||
@@ -232,6 +237,7 @@
|
||||
"oauth2_renew_secret": "Vytvořit nový tajný klíč",
|
||||
"oauth2_revoke_tokens": "Odvolat všechny klientské tokeny",
|
||||
"optional": "volitelné",
|
||||
"options": "Možnosti",
|
||||
"password": "Heslo",
|
||||
"password_length": "Délka hesla",
|
||||
"password_policy": "Politika hesel",
|
||||
@@ -337,9 +343,7 @@
|
||||
"verify": "Ověřit",
|
||||
"yes": "✓",
|
||||
"f2b_ban_time_increment": "Délka banu je prodlužována s každým dalším banem",
|
||||
"f2b_max_ban_time": "Maximální délka banu (s)",
|
||||
"ip_check": "Kontrola IP",
|
||||
"ip_check_disabled": "Kontrola IP je vypnuta. Můžete ji zapnout v <br> <strong>System > Nastavení > Options > Přizpůsobení</strong>"
|
||||
"f2b_max_ban_time": "Maximální délka banu (s)"
|
||||
},
|
||||
"danger": {
|
||||
"access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři",
|
||||
@@ -443,6 +447,9 @@
|
||||
"target_domain_invalid": "Cílová doména %s je neplatná",
|
||||
"targetd_not_found": "Cílová doména %s nenalezena",
|
||||
"targetd_relay_domain": "Cílová doména %s je předávaná",
|
||||
"template_exists": "Šablona %s již existuje",
|
||||
"template_id_invalid": "Šablona ID %s je neplatná",
|
||||
"template_name_invalid": "Název šablony je neplatný",
|
||||
"temp_error": "Dočasná chyba",
|
||||
"text_empty": "Text nesmí být prázdný",
|
||||
"tfa_token_invalid": "Neplatný TFA token",
|
||||
@@ -461,27 +468,27 @@
|
||||
"yotp_verification_failed": "Yubico OTP ověření selhalo: %s"
|
||||
},
|
||||
"datatables": {
|
||||
"emptyTable": "Tabulka neobsahuje žádná data",
|
||||
"info": "Zobrazuji _START_ až _END_ z celkem _TOTAL_ záznamů",
|
||||
"infoEmpty": "Zobrazuji 0 až 0 z 0 záznamů",
|
||||
"infoFiltered": "(filtrováno z celkem _MAX_ záznamů)",
|
||||
"loadingRecords": "Načítám...",
|
||||
"zeroRecords": "Žádné záznamy nebyly nalezeny",
|
||||
"paginate": {
|
||||
"first": "První",
|
||||
"last": "Poslední",
|
||||
"next": "Další",
|
||||
"previous": "Předchozí"
|
||||
},
|
||||
"aria": {
|
||||
"sortAscending": ": aktivujte pro seřazení vzestupně",
|
||||
"sortDescending": ": aktivujte pro seřazení sestupně"
|
||||
},
|
||||
"lengthMenu": "Zobrazit _MENU_ výsledků",
|
||||
"processing": "Zpracovávání...",
|
||||
"search": "Vyhledávání:",
|
||||
"decimal": ",",
|
||||
"thousands": " "
|
||||
"emptyTable": "Tabulka neobsahuje žádná data",
|
||||
"info": "Zobrazuji _START_ až _END_ z celkem _TOTAL_ záznamů",
|
||||
"infoEmpty": "Zobrazuji 0 až 0 z 0 záznamů",
|
||||
"infoFiltered": "(filtrováno z celkem _MAX_ záznamů)",
|
||||
"loadingRecords": "Načítám...",
|
||||
"zeroRecords": "Žádné záznamy nebyly nalezeny",
|
||||
"paginate": {
|
||||
"first": "První",
|
||||
"last": "Poslední",
|
||||
"next": "Další",
|
||||
"previous": "Předchozí"
|
||||
},
|
||||
"aria": {
|
||||
"sortAscending": ": aktivujte pro seřazení vzestupně",
|
||||
"sortDescending": ": aktivujte pro seřazení sestupně"
|
||||
},
|
||||
"lengthMenu": "Zobrazit _MENU_ výsledků",
|
||||
"processing": "Zpracovávání...",
|
||||
"search": "Vyhledávání:",
|
||||
"decimal": ",",
|
||||
"thousands": " "
|
||||
},
|
||||
"debug": {
|
||||
"chart_this_server": "Graf (tento server)",
|
||||
@@ -670,6 +677,7 @@
|
||||
"apps": "Aplikace",
|
||||
"debug": "Systémové informace",
|
||||
"email": "E-Mail",
|
||||
"mailcow_system": "Systém",
|
||||
"mailcow_config": "Nastavení",
|
||||
"quarantine": "Karanténa",
|
||||
"restart_netfilter": "Restartovat netfilter",
|
||||
@@ -705,6 +713,7 @@
|
||||
"add_mailbox": "Přidat mailovou schránku",
|
||||
"add_recipient_map_entry": "Přidat mapu příjemce",
|
||||
"add_resource": "Přidat zdroj",
|
||||
"add_template": "Přidat šablonu",
|
||||
"add_tls_policy_map": "Přidat mapu TLS pravidel",
|
||||
"address_rewriting": "Přepisování adres",
|
||||
"alias": "Alias",
|
||||
@@ -747,6 +756,7 @@
|
||||
"domain": "Doména",
|
||||
"domain_admins": "Správci domén",
|
||||
"domain_aliases": "Doménové aliasy",
|
||||
"domain_templates": "Šablony domén",
|
||||
"domain_quota": "Kvóta",
|
||||
"domain_quota_total": "Celková kvóta domény",
|
||||
"domains": "Domény",
|
||||
@@ -775,6 +785,7 @@
|
||||
"mailbox_defaults": "Výchozí nastavení",
|
||||
"mailbox_defaults_info": "Definuje výchozí nastavení pro nové schránky",
|
||||
"mailbox_defquota": "Výchozí velikost schránky",
|
||||
"mailbox_templates": "Šablony schránek",
|
||||
"mailbox_quota": "Max. velikost schránky",
|
||||
"mailboxes": "Mailové schránky",
|
||||
"max_aliases": "Max. počet aliasů",
|
||||
@@ -842,6 +853,8 @@
|
||||
"table_size_show_n": "Zobrazit %s položek",
|
||||
"target_address": "Cílová adresa",
|
||||
"target_domain": "Cílová doména",
|
||||
"templates": "Šablony",
|
||||
"template": "Šablona",
|
||||
"tls_enforce_in": "Vynutit TLS pro příchozí",
|
||||
"tls_enforce_out": "Vynutit TLS pro odchozí",
|
||||
"tls_map_dest": "Cíl",
|
||||
@@ -1006,6 +1019,9 @@
|
||||
"settings_map_added": "Přidána položka mapování nastavení",
|
||||
"settings_map_removed": "Položka mapování nastavení: %s smazána",
|
||||
"sogo_profile_reset": "SOGo profil uživatele %s vyresetován",
|
||||
"template_added": "Přidána šablona %s",
|
||||
"template_modified": "Změny šablony %s byly uloženy",
|
||||
"template_removed": "Šablona ID %s byla odstraněna",
|
||||
"tls_policy_map_entry_deleted": "Položka mapy TLS pravidel ID %s smazána",
|
||||
"tls_policy_map_entry_saved": "Položka mapy TLS pravidel \"%s\" uložena",
|
||||
"ui_texts": "Změny UI textů uloženy",
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
"domain": "Domain",
|
||||
"domain_matches_hostname": "Domain %s darf nicht dem Hostnamen entsprechen",
|
||||
"domain_quota_m": "Domain-Speicherplatz gesamt (MiB)",
|
||||
"dry": "Synchronisation simulieren",
|
||||
"enc_method": "Verschlüsselung",
|
||||
"exclude": "Elemente ausschließen (Regex)",
|
||||
"full_name": "Vor- und Nachname",
|
||||
@@ -573,6 +574,7 @@
|
||||
"client_secret": "Client-Secret",
|
||||
"comment_info": "Ein privater Kommentar ist für den Benutzer nicht einsehbar. Ein öffentlicher Kommentar wird als Tooltip im Interface des Benutzers angezeigt.",
|
||||
"created_on": "Erstellt am",
|
||||
"custom_attributes": "benutzerdefinierte Attribute",
|
||||
"delete1": "Lösche Nachricht nach Übertragung vom Quell-Server",
|
||||
"delete2": "Lösche Nachrichten von Ziel-Server, die nicht auf Quell-Server vorhanden sind",
|
||||
"delete2duplicates": "Lösche Duplikate im Ziel",
|
||||
@@ -613,6 +615,7 @@
|
||||
"max_quota": "Max. Größe per Mailbox (MiB)",
|
||||
"maxage": "Maximales Alter in Tagen einer Nachricht, die kopiert werden soll<br><small>(0 = alle Nachrichten kopieren)</small>",
|
||||
"maxbytespersecond": "Max. Übertragungsrate in Bytes/s (0 für unlimitiert)",
|
||||
"mbox_exclude": "Mailboxen ausschließen",
|
||||
"mbox_rl_info": "Dieses Limit wird auf den SASL Loginnamen angewendet und betrifft daher alle Absenderadressen, die der eingeloggte Benutzer verwendet. Bei Mailbox Ratelimit überwiegt ein Domain-weites Ratelimit.",
|
||||
"mins_interval": "Intervall (min)",
|
||||
"multiple_bookings": "Mehrfaches Buchen",
|
||||
@@ -857,7 +860,7 @@
|
||||
"sieve_preset_5": "Auto-Responder (Vacation, Urlaub)",
|
||||
"sieve_preset_6": "E-Mails mit Nachricht abweisen",
|
||||
"sieve_preset_7": "Weiterleiten und behalten oder verwerfen",
|
||||
"sieve_preset_8": "Nachricht verwerfen, wenn Absender und Alias-Ziel identisch sind.",
|
||||
"sieve_preset_8": "E-Mail eines bestimmten Absenders umleiten, als gelesen markieren und in Unterordner sortieren",
|
||||
"sieve_preset_header": "Beispielinhalte zur Einsicht stehen nachstehend bereit. Siehe auch <a href=\"https://de.wikipedia.org/wiki/Sieve\" target=\"_blank\">Wikipedia</a>.",
|
||||
"sogo_visible": "Alias Sichtbarkeit in SOGo",
|
||||
"sogo_visible_n": "Alias in SOGo verbergen",
|
||||
@@ -1124,6 +1127,7 @@
|
||||
"apple_connection_profile_complete": "Dieses Verbindungsprofil beinhaltet neben IMAP- und SMTP-Konfigurationen auch Pfade für die Konfiguration von CalDAV (Kalender) und CardDAV (Adressbücher) für ein Apple-Gerät.",
|
||||
"apple_connection_profile_mailonly": "Dieses Verbindungsprofil beinhaltet IMAP- und SMTP-Konfigurationen für ein Apple-Gerät.",
|
||||
"apple_connection_profile_with_app_password": "Es wird ein neues App-Passwort erzeugt und in das Profil eingefügt, damit bei der Einrichtung kein Passwort eingegeben werden muss. Geben Sie das Profil nicht weiter, da es einen vollständigen Zugriff auf Ihr Postfach ermöglicht.",
|
||||
"attribute": "Attribut",
|
||||
"change_password": "Passwort ändern",
|
||||
"change_password_hint_app_passwords": "Ihre Mailbox hat %d App-Passwörter, die nicht geändert werden. Um diese zu verwalten, gehen Sie bitte zum App-Passwörter-Tab.",
|
||||
"clear_recent_successful_connections": "Alle erfolgreichen Verbindungen bereinigen",
|
||||
@@ -1243,6 +1247,7 @@
|
||||
"tls_policy_warning": "<strong>Vorsicht:</strong> Entscheiden Sie sich unverschlüsselte Verbindungen abzulehnen, kann dies dazu führen, dass Kontakte Sie nicht mehr erreichen.<br>Nachrichten, die die Richtlinie nicht erfüllen, werden durch einen Hard-Fail im Mailsystem abgewiesen.<br>Diese Einstellung ist aktiv für die primäre Mailbox, für alle Alias-Adressen, die dieser Mailbox <b>direkt zugeordnet</b> sind (lediglich eine einzige Ziel-Adresse) und der Adressen, die sich aus Alias-Domains ergeben. Ausgeschlossen sind temporäre Aliasse (\"Spam-Alias-Adressen\"), Catch-All Alias-Adressen sowie Alias-Adressen mit mehreren Zielen.",
|
||||
"user_settings": "Benutzereinstellungen",
|
||||
"username": "Benutzername",
|
||||
"value": "Wert",
|
||||
"verify": "Verifizieren",
|
||||
"waiting": "Warte auf Ausführung",
|
||||
"week": "Woche",
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
"domain": "Domain",
|
||||
"domain_matches_hostname": "Domain %s matches hostname",
|
||||
"domain_quota_m": "Total domain quota (MiB)",
|
||||
"dry": "Simulate synchronization",
|
||||
"enc_method": "Encryption method",
|
||||
"exclude": "Exclude objects (regex)",
|
||||
"full_name": "Full name",
|
||||
@@ -575,6 +576,7 @@
|
||||
"client_secret": "Client secret",
|
||||
"comment_info": "A private comment is not visible to the user, while a public comment is shown as tooltip when hovering it in a user's overview",
|
||||
"created_on": "Created on",
|
||||
"custom_attributes": "Custom attributes",
|
||||
"delete1": "Delete from source when completed",
|
||||
"delete2": "Delete messages on destination that are not on source",
|
||||
"delete2duplicates": "Delete duplicates on destination",
|
||||
@@ -591,7 +593,8 @@
|
||||
"from_user": "{= from_user =} - From user part of envelope, e.g for \"moo@mailcow.tld\" it returns \"moo\"",
|
||||
"from_name": "{= from_name =} - From name of envelope, e.g for \"Mailcow <moo@mailcow.tld>\" it returns \"Mailcow\"",
|
||||
"from_addr": "{= from_addr =} - From address part of envelope",
|
||||
"from_domain": "{= from_domain =} - From domain part of envelope"
|
||||
"from_domain": "{= from_domain =} - From domain part of envelope",
|
||||
"custom": "{= foo =} - If mailbox has the custom attribute \"foo\" with value \"bar\" it returns \"bar\""
|
||||
},
|
||||
"domain_footer_plain": "PLAIN footer",
|
||||
"domain_quota": "Domain quota",
|
||||
@@ -622,6 +625,7 @@
|
||||
"max_quota": "Max. quota per mailbox (MiB)",
|
||||
"maxage": "Maximum age of messages in days that will be polled from remote<br><small>(0 = ignore age)</small>",
|
||||
"maxbytespersecond": "Max. bytes per second <br><small>(0 = unlimited)</small>",
|
||||
"mbox_exclude": "Exclude mailboxes",
|
||||
"mbox_rl_info": "This rate limit is applied on the SASL login name, it matches any \"from\" address used by the logged-in user. A mailbox rate limit overrides a domain-wide rate limit.",
|
||||
"mins_interval": "Interval (min)",
|
||||
"multiple_bookings": "Multiple bookings",
|
||||
@@ -873,7 +877,7 @@
|
||||
"sieve_preset_5": "Auto responder (vacation)",
|
||||
"sieve_preset_6": "Reject mail with reponse",
|
||||
"sieve_preset_7": "Redirect and keep/drop",
|
||||
"sieve_preset_8": "Discard message sent to an alias address the sender is part of",
|
||||
"sieve_preset_8": "Redirect e-mail from a specific sender, mark as read and sort into subfolder",
|
||||
"sieve_preset_header": "Please see the example presets below. For more details see <a href=\"https://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)\" target=\"_blank\">Wikipedia</a>.",
|
||||
"sogo_visible": "Alias is visible in SOGo",
|
||||
"sogo_visible_n": "Hide alias in SOGo",
|
||||
@@ -1140,6 +1144,7 @@
|
||||
"apple_connection_profile_complete": "This connection profile includes IMAP and SMTP parameters as well as CalDAV (calendars) and CardDAV (contacts) paths for an Apple device.",
|
||||
"apple_connection_profile_mailonly": "This connection profile includes IMAP and SMTP configuration parameters for an Apple device.",
|
||||
"apple_connection_profile_with_app_password": "A new app password is generated and added to the profile so that no password needs to be entered when setting up your device. Please do not share the file as it grants full access to your mailbox.",
|
||||
"attribute": "Attribute",
|
||||
"change_password": "Change password",
|
||||
"change_password_hint_app_passwords": "Your account has %d app passwords that will not be changed. To manage these, go to the App passwords tab.",
|
||||
"clear_recent_successful_connections": "Clear seen successful connections",
|
||||
@@ -1270,6 +1275,7 @@
|
||||
"tls_policy_warning": "<strong>Warning:</strong> If you decide to enforce encrypted mail transfer, you may lose emails.<br>Messages to not satisfy the policy will be bounced with a hard fail by the mail system.<br>This option applies to your primary email address (login name), all addresses derived from alias domains as well as alias addresses <b>with only this single mailbox</b> as target.",
|
||||
"user_settings": "User settings",
|
||||
"username": "Username",
|
||||
"value": "Value",
|
||||
"verify": "Verify",
|
||||
"waiting": "Waiting",
|
||||
"week": "week",
|
||||
|
||||
@@ -891,5 +891,17 @@
|
||||
"no_active_admin": "Viimeistä aktiivista järjestelmänvalvojaa ei voi poistaa käytöstä",
|
||||
"session_token": "Lomakkeen tunnus sanoma ei kelpaa: tunnus sanoman risti riita",
|
||||
"session_ua": "Lomakkeen tunnus sanoma ei kelpaa: käyttäjä agentin tarkistus virhe"
|
||||
},
|
||||
"datatables": {
|
||||
"emptyTable": "Tietoja ei ole saatavilla taulukossa",
|
||||
"expand_all": "Laajenna kaikki",
|
||||
"lengthMenu": "Näytä menu merkinnät",
|
||||
"loadingRecords": "Ladataan...",
|
||||
"processing": "Ole hyvä ja odota...",
|
||||
"search": "Etsi:",
|
||||
"paginate": {
|
||||
"first": "Ensimmäinen",
|
||||
"last": "Edellinen"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Vous pouvez définir des cartes de transport vers une destination personnalisée pour ce domaine. sinon, une recherche MX sera effectuée.",
|
||||
"relay_unknown_only": "Relayer uniquement les boîtes inexistantes. Les boîtes existantes seront livrées localement.",
|
||||
"relayhost_wrapped_tls_info": "Veuillez <b>ne pas</b> utiliser des ports TLS wrappés (généralement utilisés sur le port 465).<br>\r\nUtilisez n'importe quel port non encapsulé et lancez STARTTLS. Une politique TLS pour appliquer TLS peut être créée dans \"Cartes de politique TLS\".",
|
||||
"select": "Veuillez sélectionner...",
|
||||
"select": "Veuillez sélectionner…",
|
||||
"select_domain": "Sélectionner d'abord un domaine",
|
||||
"sieve_desc": "Description courte",
|
||||
"sieve_type": "Type de filtre",
|
||||
@@ -207,7 +207,7 @@
|
||||
"last_applied": "Dernière application",
|
||||
"license_info": "Une licence n’est pas requise, mais contribue au développement.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Enregistrer votre GUID ici</a> or <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Support order\">acheter le support pour votre intallation Mailcow.</a>",
|
||||
"link": "Lien",
|
||||
"loading": "Veuillez patienter...",
|
||||
"loading": "Veuillez patienter…",
|
||||
"logo_info": "Votre image sera redimensionnée à une hauteur de 40 pixels pour la barre de navigation du haut et à un maximum de 250 pixels en largeur pour la page d'accueil. Un graphique extensible est fortement recommandé.",
|
||||
"lookup_mx": "Faire correspondre la destination à MX (.outlook.com pour acheminer tous les messages ciblés vers un MX * .outlook.com sur ce tronçon)",
|
||||
"main_name": "\"mailcow UI\" nom",
|
||||
@@ -326,7 +326,11 @@
|
||||
"password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules",
|
||||
"password_policy_numbers": "Doit contenir au moins un chiffre",
|
||||
"ip_check": "Vérification IP",
|
||||
"ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>"
|
||||
"ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>",
|
||||
"logo_normal_label": "Normal",
|
||||
"logo_dark_label": "Inversé pour le mode sombre",
|
||||
"allowed_methods": "Access-Control-Allow-Methods",
|
||||
"allowed_origins": "Access-Control-Allow-Origin"
|
||||
},
|
||||
"danger": {
|
||||
"access_denied": "Accès refusé ou données de formulaire non valides",
|
||||
@@ -598,9 +602,9 @@
|
||||
"delete_these_items": "Veuillez confirmer les modifications apportées à l’identifiant d’objet suivant",
|
||||
"hibp_nok": "Trouvé ! Il s’agit d’un mot de passe potentiellement dangereux !",
|
||||
"hibp_ok": "Aucune correspondance trouvée.",
|
||||
"loading": "Veuillez patienter...",
|
||||
"loading": "Veuillez patienter…",
|
||||
"restart_container": "Redémarrer le conteneur",
|
||||
"restart_container_info": "<b>Important:</b> Un redémarrage en douceur peut prendre un certain temps, veuillez attendre qu’il soit terminé..",
|
||||
"restart_container_info": "<b>Important:</b> Un redémarrage en douceur peut prendre un certain temps, veuillez attendre qu’il soit terminé.",
|
||||
"restart_now": "Redémarrer maintenant",
|
||||
"restarting_container": "Redémarrage du conteneur, cela peut prendre un certain temps"
|
||||
},
|
||||
@@ -935,7 +939,7 @@
|
||||
"disable_tfa": "Désactiver TFA jusqu’à la prochaine ouverture de session réussie",
|
||||
"enter_qr_code": "Votre code TOTP si votre appareil ne peut pas scanner les codes QR",
|
||||
"error_code": "Code d'erreur",
|
||||
"init_webauthn": "Initialisation, veuillez patienter...",
|
||||
"init_webauthn": "Initialisation, veuillez patienter…",
|
||||
"key_id": "Un identifiant pour votre Périphérique",
|
||||
"key_id_totp": "Un identifiant pour votre clé",
|
||||
"none": "Désactiver",
|
||||
@@ -948,8 +952,8 @@
|
||||
"tfa_token_invalid": "Token TFA invalide",
|
||||
"totp": "OTP (One Time Password = Mot de passe à usage unique : Google Authenticator, Authy, etc.)",
|
||||
"webauthn": "Authentification WebAuthn",
|
||||
"waiting_usb_auth": "<i>En attente d’un périphérique USB...</i><br><br>S’il vous plaît appuyez maintenant sur le bouton de votre périphérique USB WebAuthn.",
|
||||
"waiting_usb_register": "<i>En attente d’un périphérique USB...</i><br><br>Veuillez entrer votre mot de passe ci-dessus et confirmer votre inscription WebAuthn en appuyant sur le bouton de votre périphérique USB WebAuthn.",
|
||||
"waiting_usb_auth": "<i>En attente d’un périphérique USB…</i><br><br>S’il vous plaît appuyez maintenant sur le bouton de votre périphérique USB WebAuthn.",
|
||||
"waiting_usb_register": "<i>En attente d’un périphérique USB…</i><br><br>Veuillez entrer votre mot de passe ci-dessus et confirmer votre inscription WebAuthn en appuyant sur le bouton de votre périphérique USB WebAuthn.",
|
||||
"yubi_otp": "Authentification OTP Yubico"
|
||||
},
|
||||
"fido2": {
|
||||
@@ -999,9 +1003,9 @@
|
||||
"eas_reset": "Réinitialiser le cache de l’appareil Activesync",
|
||||
"eas_reset_help": "Dans de nombreux cas, une réinitialisation du cache de l’appareil aidera à récupérer un profil Activesync cassé.<br><b>Attention :</b> Tous les éléments seront à nouveau téléchargés !",
|
||||
"eas_reset_now": "Réinitialiser maintenant",
|
||||
"edit": "Editer",
|
||||
"email": "Email",
|
||||
"email_and_dav": "Email, calendriers et contacts",
|
||||
"edit": "Éditer",
|
||||
"email": "E-mail",
|
||||
"email_and_dav": "E-mail, calendriers et contacts",
|
||||
"encryption": "Cryptage",
|
||||
"excludes": "Exclut",
|
||||
"expire_in": "Expire dans",
|
||||
@@ -1015,7 +1019,7 @@
|
||||
"is_catch_all": "Attrape-tout pour le domaine(s)",
|
||||
"last_mail_login": "Dernière connexion mail",
|
||||
"last_run": "Dernière exécution",
|
||||
"loading": "Chargement...",
|
||||
"loading": "Chargement…",
|
||||
"mailbox_details": "Détails de la boîte",
|
||||
"messages": "messages",
|
||||
"never": "jamais",
|
||||
@@ -1051,7 +1055,7 @@
|
||||
"shared_aliases": "Adresses alias partagées",
|
||||
"shared_aliases_desc": "Les alias partagés ne sont pas affectés par les paramètres spécifiques à l’utilisateur tels que le filtre anti-spam ou la politique de chiffrement. Les filtres anti-spam correspondants ne peuvent être effectués que par un administrateur en tant que politique de domaine.",
|
||||
"show_sieve_filters": "Afficher le filtre de tamis actif de l’utilisateur",
|
||||
"sogo_profile_reset": "Remise é zéro du profil SOGo",
|
||||
"sogo_profile_reset": "Remise à zéro du profil SOGo",
|
||||
"sogo_profile_reset_help": "Ceci détruira un profil Sogo des utilisateurs et <b>supprimera toutes les données de contact et de calendrier irrécupérables</b>.",
|
||||
"sogo_profile_reset_now": "Remise à zéro du profil maintenant",
|
||||
"spam_aliases": "Alias de courriel temporaire",
|
||||
|
||||
@@ -394,7 +394,12 @@
|
||||
"filters": "Szűrők",
|
||||
"login_as": "Bejelentkezés mint",
|
||||
"quarantine": "Karantén műveletek",
|
||||
"bcc_maps": "BCC címek"
|
||||
"bcc_maps": "BCC címek",
|
||||
"domain_relayhost": "Relayhost megváltoztatása egy domainhez",
|
||||
"protocol_access": "Protokoll-hozzáférés módosítása",
|
||||
"quarantine_attachments": "Karantén mellékletek",
|
||||
"quarantine_category": "Karantén értesítési kategória módosítása",
|
||||
"quarantine_notification": "Karantén értesítések módosítása"
|
||||
},
|
||||
"diagnostics": {
|
||||
"dns_records": "DNS bejegyzések"
|
||||
|
||||
1300
data/web/lang/lang.pt-br.json
Normal file
1300
data/web/lang/lang.pt-br.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -107,7 +107,8 @@
|
||||
"validate": "Проверить",
|
||||
"validation_success": "Проверка прошла успешно",
|
||||
"tags": "Теги",
|
||||
"app_passwd_protocols": "Разрешенные протоколы для пароля приложения"
|
||||
"app_passwd_protocols": "Разрешенные протоколы для пароля приложения",
|
||||
"dry": "Имитировать синхронизацию"
|
||||
},
|
||||
"admin": {
|
||||
"access": "Настройки доступа",
|
||||
@@ -625,11 +626,14 @@
|
||||
"auth_user": "{= auth_user =} - Аутентифицированное имя пользователя, указанное MTA",
|
||||
"from_user": "{= from_user =} - Из пользовательской части envelope, например, для \"moo@mailcow.tld\" возвращается \"moo\"",
|
||||
"from_addr": "{= from_addr =} - Из адресной части envelope",
|
||||
"from_domain": "{= from_domain =} - из доменной части envelope"
|
||||
"from_domain": "{= from_domain =} - из доменной части envelope",
|
||||
"custom": "{= foo =} - Если почтовый ящик имеет пользовательский атрибут \"foo\" со значением \"bar\", он возвращает \"bar\"."
|
||||
},
|
||||
"domain_footer": "Нижний колонтитул домена",
|
||||
"domain_footer_html": "HTML нижний колонтитул",
|
||||
"domain_footer_plain": "ПРОСТОЙ нижний колонтитул"
|
||||
"domain_footer_plain": "ПРОСТОЙ нижний колонтитул",
|
||||
"mbox_exclude": "Исключить почтовые ящики",
|
||||
"custom_attributes": "Пользовательские атрибуты"
|
||||
},
|
||||
"fido2": {
|
||||
"confirm": "Подтвердить",
|
||||
@@ -1198,7 +1202,9 @@
|
||||
"apple_connection_profile_with_app_password": "Новый пароль приложения генерируется и добавляется в профиль, поэтому при настройке устройства не требуется вводить пароль. Не предоставляйте доступ к файлу, поскольку он предоставляет полный доступ к вашему почтовому ящику.",
|
||||
"direct_protocol_access": "Этот пользователь почтового ящика имеет <b>прямой, внешний доступ</b> к следующим протоколам и приложениям. Эта настройка контролируется вашим администратором. Для предоставления доступа к отдельным протоколам и приложениям могут быть созданы пароли приложений.<br> Кнопка \"Вход в веб-почту\" обеспечивает единый вход в SOGo и всегда доступна.",
|
||||
"with_app_password": "с паролем приложения",
|
||||
"change_password_hint_app_passwords": "В вашей учетной записи есть {{number_of_app_passwords}} паролей приложений, которые не будут изменены. Чтобы управлять ими, перейдите на вкладку \"Пароли приложений\"."
|
||||
"change_password_hint_app_passwords": "В вашей учетной записи есть {{number_of_app_passwords}} паролей приложений, которые не будут изменены. Чтобы управлять ими, перейдите на вкладку \"Пароли приложений\".",
|
||||
"attribute": "Атрибут",
|
||||
"value": "Значение"
|
||||
},
|
||||
"warning": {
|
||||
"cannot_delete_self": "Вы не можете удалить сами себя",
|
||||
|
||||
@@ -342,7 +342,12 @@
|
||||
"username": "Uporabniško ime",
|
||||
"validate_license_now": "Potrdi GUID z licenčnim strežnikom",
|
||||
"verify": "Preveri",
|
||||
"yes": "✓"
|
||||
"yes": "✓",
|
||||
"logo_normal_label": "Navadno",
|
||||
"logo_dark_label": "Za temni način",
|
||||
"cors_settings": "Nastavitve CORS",
|
||||
"allowed_methods": "Dovoljene metode za upravljanje dostopa",
|
||||
"allowed_origins": "Upravljanje-dostopa-Dovoljeni-Viri"
|
||||
},
|
||||
"danger": {
|
||||
"alias_goto_identical": "Alias in goto naslov morata biti identična",
|
||||
@@ -391,6 +396,161 @@
|
||||
"invalid_destination": "Ciljna oblika \"%s\" ni veljavna",
|
||||
"invalid_filter_type": "Neveljavna vrsta filtra",
|
||||
"invalid_host": "Naveden je neveljaven gostitelj (host): %s",
|
||||
"invalid_mime_type": "Neveljaven mime type"
|
||||
"invalid_mime_type": "Neveljaven mime type",
|
||||
"max_quota_in_use": "Kvota poštnega predala mora biti večja ali enaka %d MB",
|
||||
"password_complexity": "Geslo ne ustreza varnostni politiki",
|
||||
"pushover_credentials_missing": "Manjka Pushover token ali ključ",
|
||||
"release_send_failed": "Sporočila ni bilo mogoče sprostiti: %s",
|
||||
"tls_policy_map_dest_invalid": "Cilj politike ni veljaven",
|
||||
"webauthn_authenticator_failed": "Izbrani avtentikator ni bil najden",
|
||||
"reset_f2b_regex": "Regex filter ni bilo možno ponastaviti v ustreznem času. Prosim poskusite ponovno ali počakajte nekaj sekund in ponovno naložite stran.",
|
||||
"target_domain_invalid": "Ciljna domena %s ni veljavna",
|
||||
"validity_missing": "Prosim nastavite obdobje veljavnosti",
|
||||
"invalid_recipient_map_old": "Naveden neveljaven izvirni prejemnik: %s",
|
||||
"ip_list_empty": "Seznam dovoljenih IPjev ne sme biti prazen",
|
||||
"is_alias": "%s je že znan kot alias naslov",
|
||||
"is_alias_or_mailbox": "%s je že znan kot alias, poštni naslov, ali alias izveden iz alias domene",
|
||||
"is_spam_alias": "%s že obstaja kot začasen alias (spam alias naslov)",
|
||||
"last_key": "Zadnji ključ ne more biti izbrisan, prosim raje deaktivirajte dvofaktorsko avtentikacijo (TFA)",
|
||||
"login_failed": "Prijava ni uspela",
|
||||
"mailbox_defquota_exceeds_mailbox_maxquota": "Privzeta kvota presega najvišjo omejitev",
|
||||
"mailbox_invalid": "Ime poštnega predala ni veljavno",
|
||||
"mailbox_quota_exceeded": "Kvota presega omejitev domene (maksimalno %d MB)",
|
||||
"mailbox_quota_exceeds_domain_quota": "Najvišja kvota presega omejitev domene",
|
||||
"mailbox_quota_left_exceeded": "Ni dovolj prostora (preostali prostor: %d MB)",
|
||||
"mailboxes_in_use": "Največje število poštnih predalov mora biti večje ali enako %d",
|
||||
"malformed_username": "Nepravilno oblikovano uporabniško ime",
|
||||
"map_content_empty": "Preslikava vsebine ne more biti prazna",
|
||||
"max_alias_exceeded": "Preseženo največje število aliasov",
|
||||
"max_mailbox_exceeded": "Preseženo največje število poštnih predalov (%d od %d)",
|
||||
"maxquota_empty": "Največja kvota na poštni predal ne more biti 0",
|
||||
"mysql_error": "Napaka MySQL: %s",
|
||||
"network_host_invalid": "Nepravilno omrežje ali gostitel: %s",
|
||||
"next_hop_interferes": "% moti naslednji skok %s",
|
||||
"next_hop_interferes_any": "Obstoječi naslednji skok moti %s",
|
||||
"nginx_reload_failed": "Ponovni zagon Nginx ni uspel: %s",
|
||||
"no_user_defined": "Uporabnik ni določen",
|
||||
"object_exists": "Objekt %s že obstaja",
|
||||
"object_is_not_numeric": "Vrednost %s ni numerična",
|
||||
"password_empty": "Geslo ne sme biti prazno",
|
||||
"password_mismatch": "Potrditev gesla se ne ujema z geslom",
|
||||
"policy_list_from_exists": "Zapis z tem imenom že obstaja",
|
||||
"policy_list_from_invalid": "Zapis ima nepravilno obliko",
|
||||
"private_key_error": "Napaka zasebnega ključa: %s",
|
||||
"pushover_key": "Pushover ključ ni v pravilni obliki",
|
||||
"pushover_token": "Pushover token ni v pravilni obliki",
|
||||
"quota_not_0_not_numeric": "Quota mora biti število in večje ali enako 0",
|
||||
"recipient_map_entry_exists": "Preslikava prejemnika \"%s\" že obstaja",
|
||||
"redis_error": "Napaka Redis: %s",
|
||||
"relayhost_invalid": "Vnos preslikave %s ni pravilen",
|
||||
"resource_invalid": "Ime vira je neveljavno",
|
||||
"rl_timeframe": "Časovni okvir za rate limit je nepravilen",
|
||||
"rspamd_ui_pw_length": "Rspamd UI geslo mora biti dolgo vsaj 6 znakov",
|
||||
"script_empty": "Script ne more biti prazen",
|
||||
"sender_acl_invalid": "Vrednost ACL pošiljatelja %s ni veljavna",
|
||||
"set_acl_failed": "Ni uspelo nastaviti ACL",
|
||||
"settings_map_invalid": "ID preslikave nastavitev %s ni veljaven",
|
||||
"sieve_error": "Napaka Sieve parserja: %s",
|
||||
"spam_learn_error": "Napaka pri učenju spama: %s",
|
||||
"subject_empty": "Predmet ne sme biti prazno",
|
||||
"targetd_not_found": "Ciljna domena %s ni bila najdena",
|
||||
"targetd_relay_domain": "Ciljna domena %s je relay domena",
|
||||
"template_exists": "Predloga %s že obstaja",
|
||||
"template_id_invalid": "ID predloge %s ni veljaven",
|
||||
"template_name_invalid": "Ime predloge ni veljavno",
|
||||
"text_empty": "Besedilo ne sme biti prazno",
|
||||
"tfa_token_invalid": "Neveljaven token TFA",
|
||||
"tls_policy_map_entry_exists": "Vpis preslikave TLS \"%s\" že obstaja",
|
||||
"tls_policy_map_parameter_invalid": "Parameter politike ni pravilen",
|
||||
"totp_verification_failed": "Neuspešno preverjanje TOTP",
|
||||
"transport_dest_exists": "Cilj transporta \"%s\" že obstaja",
|
||||
"webauthn_verification_failed": "Preverjanje WebAuthn ni uspelo: %s",
|
||||
"webauthn_publickey_failed": "Na izbranem avtentikatorju ni shranjenega javnega ključa",
|
||||
"webauthn_username_failed": "Izbrani avtentikator pripada drugemu uporabniškemu računu",
|
||||
"unknown": "Pojavila se je neznana napaka",
|
||||
"unknown_tfa_method": "Neznana metoda TFA",
|
||||
"unlimited_quota_acl": "Neomejena kvota je prepovedana z ACL",
|
||||
"username_invalid": "Uporabniško ime %s ne more biti uporabljeno",
|
||||
"value_missing": "Prosim vnesite vse vrednosti",
|
||||
"yotp_verification_failed": "Preverjanje Yubico OTP ni uspelo: %s",
|
||||
"temp_error": "Začasna napaka",
|
||||
"cors_invalid_method": "Navedena neveljavna Allow metoda",
|
||||
"cors_invalid_origin": "Naveden neveljaven Allow-Origin",
|
||||
"invalid_recipient_map_new": "Naveden neveljaven nov prejemnik: %s"
|
||||
},
|
||||
"debug": {
|
||||
"containers_info": "Informacije o vsebniku (containerju)",
|
||||
"architecture": "Arhitektura",
|
||||
"chart_this_server": "Diagram (ta strežnik)",
|
||||
"container_running": "Aktiven",
|
||||
"container_disabled": "Ustavljen ali onemogočen",
|
||||
"container_stopped": "Ustavljen",
|
||||
"cores": "Jedra",
|
||||
"current_time": "Sistemski čas",
|
||||
"disk_usage": "Zasedenost diska",
|
||||
"docs": "Dokumenti",
|
||||
"error_show_ip": "Ni mogoče preveriti javnega IP naslova",
|
||||
"external_logs": "Zunanji dnevniki",
|
||||
"last_modified": "Nazadnje spremenjeno",
|
||||
"history_all_servers": "Zgodovina (vsi strežniki)",
|
||||
"in_memory_logs": "In-memory dnevniki",
|
||||
"jvm_memory_solr": "JVM zasedenost spomina",
|
||||
"service": "Servis",
|
||||
"show_ip": "Prikaži javni IP",
|
||||
"size": "Velikost",
|
||||
"solr_dead": "Solr se zaganja, je onemogočen ali se je ustavil.",
|
||||
"solr_status": "Status Solr",
|
||||
"started_at": "Zagnano ob",
|
||||
"started_on": "Zagnano na",
|
||||
"static_logs": "Statični dnevniki",
|
||||
"success": "Uspešno",
|
||||
"system_containers": "Sistem in Containerji",
|
||||
"timezone": "Časovni pas",
|
||||
"uptime": "Čas delovanja",
|
||||
"update_available": "Posodobitev je na voljo",
|
||||
"no_update_available": "Sistem je na najnovejši verziji",
|
||||
"update_failed": "Ni mogoče preveriti za posodobitve",
|
||||
"username": "Uporabniško ime",
|
||||
"wip": "Trenutno v delu"
|
||||
},
|
||||
"datatables": {
|
||||
"infoFiltered": "(filtrirano od _MAX_ skupaj zapisov)",
|
||||
"collapse_all": "Strni vse",
|
||||
"decimal": ",",
|
||||
"emptyTable": "Ni podatkov",
|
||||
"expand_all": "Razširi vse",
|
||||
"info": "Prikazano _START_ do _END_ od _TOTAL_ zapisov",
|
||||
"infoEmpty": "Prikazano 0 do 0 od 0 zapisov",
|
||||
"thousands": ".",
|
||||
"lengthMenu": "Prikaži _MENU_ zapise",
|
||||
"loadingRecords": "Nalaganje...",
|
||||
"processing": "Prosim počakajte...",
|
||||
"search": "Iskanje:",
|
||||
"zeroRecords": "Ni ujemajočih zapisov",
|
||||
"paginate": {
|
||||
"first": "Prva",
|
||||
"last": "Zadnja",
|
||||
"previous": "Prejšnja",
|
||||
"next": "Naslednja"
|
||||
},
|
||||
"aria": {
|
||||
"sortAscending": ": aktivirajte za razvrstitev stolpca naraščajoče",
|
||||
"sortDescending": ": aktivirajte za razvrstitev stolpca padajoče"
|
||||
}
|
||||
},
|
||||
"diagnostics": {
|
||||
"cname_from_a": "Vrednost pridobljena iz A/AAAA zapisa. To je podprto, če zapis kaže na pravilen resurs.",
|
||||
"dns_records": "DNS zapisi",
|
||||
"dns_records_24hours": "Prosim upoštevajte, da lahko traja do 24 ur da se spremembe v DNS pravilno prikažejo na tej strani. Namen je da lahko enostavno vidite, kako konfigurirati svoje DNS zapise in preverite ali so vaši zapisi pravilno shranjeni v DNS.",
|
||||
"dns_records_data": "Pravilni podatki",
|
||||
"dns_records_docs": "Prosim preverite tudi <a target=\"_blank\" href=\"https://docs.mailcow.email/prerequisite/prerequisite-dns/\">dokumentacijo</a>.",
|
||||
"dns_records_name": "Ime",
|
||||
"dns_records_status": "Trenutno stanje",
|
||||
"dns_records_type": "Vrsta",
|
||||
"optional": "Ta zapis je opcijski."
|
||||
},
|
||||
"edit": {
|
||||
"acl": "ACL (Dovoljenje)",
|
||||
"active": "Aktivno"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"alias_domain": "Alias doména",
|
||||
"alias_domain_info": "<small>Len platné mená domén (oddelené čiarkou).</small>",
|
||||
"app_name": "Meno aplikácie",
|
||||
"app_passwd_protocols": "Povolené protokoly pre heslá aplikácií",
|
||||
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie",
|
||||
"app_password": "Pridať heslo aplikácie",
|
||||
"automap": "Skúsiť automaticky mapovať priečinky (\"Sent items\", \"Sent\" => \"Sent\" atd.)",
|
||||
"backup_mx_options": "Možnosti preposielania",
|
||||
@@ -107,7 +107,6 @@
|
||||
"username": "Používateľské meno",
|
||||
"validate": "Overiť",
|
||||
"validation_success": "Úspešne overené",
|
||||
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie",
|
||||
"tags": "Štítky"
|
||||
},
|
||||
"admin": {
|
||||
@@ -148,6 +147,8 @@
|
||||
"ays": "Naozaj chcete pokračovať?",
|
||||
"ban_list_info": "Zoznam zakázaných IP je zobrazený nižšie: <b>sieť (zostávajúci čas zákazu) - [akcia]</b>.<br />IP adresy zaradené na unban budú odstránené z aktívneho zoznamu v priebehu niekoľkých sekúnd.<br />Červené položky zobrazujú permanentné blokovanie.",
|
||||
"change_logo": "Zmeniť logo",
|
||||
"logo_normal_label": "Normálne",
|
||||
"logo_dark_label": "Inverzné pre tmavý režim",
|
||||
"configuration": "Konfigurácia",
|
||||
"convert_html_to_text": "Konvertovať HTML do obyčajného textu",
|
||||
"credentials_transport_warning": "<b>Upozornenie</b>: Pridaním ďalšieho záznamu do transportnej mapy bude mať za následok aktualizovanie údajov pre všetky záznamy so zhodným ďalším skokom.",
|
||||
@@ -207,6 +208,9 @@
|
||||
"include_exclude": "Zahrnúť/Vylúčiť",
|
||||
"include_exclude_info": "Ak nič nevyberiete tak bude adresované <b>všetkým schránkam</b>",
|
||||
"includes": "Zahrnúť týchto príjemcov",
|
||||
"ip_check": "Kontrola IP",
|
||||
"ip_check_disabled": "Kontrola IP je vypnutá. Môžete ju zapnúť v ponuke<br> <strong>Systém > Konfigurácia > Možnosti > Prispôsobiť</strong>",
|
||||
"ip_check_opt_in": "Prihlásiť sa k používaniu služby tretej strany <strong>ipv4.mailcow.email</strong> a <strong>ipv6.mailcow.email</strong> za účelom zistenia externých IP adries.",
|
||||
"is_mx_based": "Na základe MX",
|
||||
"last_applied": "Naposledy aplikované",
|
||||
"license_info": "Licencia nie je potrebná, ale napomáha ďalšiemu vývoju.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Registrujte váš GUID tu</a> alebo <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Objednávka podpory\">zakúpte si podporu pre vašu mailcow inštaláciu.</a>",
|
||||
@@ -233,6 +237,7 @@
|
||||
"oauth2_renew_secret": "Vygenerovať nový tajný kľuč",
|
||||
"oauth2_revoke_tokens": "Odobrať všetky tokeny klienta",
|
||||
"optional": "voliteľné",
|
||||
"options": "Možnosti",
|
||||
"password": "Heslo",
|
||||
"password_length": "Dĺžka hesla",
|
||||
"password_policy": "Politika hesiel",
|
||||
@@ -440,6 +445,9 @@
|
||||
"target_domain_invalid": "Cieľová doména %s je neplatná",
|
||||
"targetd_not_found": "Cieľová doména %s sa nenašla",
|
||||
"targetd_relay_domain": "Cieľová doména %s je posielaná ďalej (relay)",
|
||||
"template_exists": "Šablóna %s už existuje",
|
||||
"template_id_invalid": "Šablóna ID %s je neplatná",
|
||||
"template_name_invalid": "Názov šablóny je neplatný",
|
||||
"temp_error": "Dočasná chyba",
|
||||
"text_empty": "Text nemôže byť prázdny",
|
||||
"tfa_token_invalid": "Neplatný TFA token",
|
||||
@@ -529,7 +537,7 @@
|
||||
"allowed_protocols": "Povolené protokoly",
|
||||
"app_name": "Meno aplikácie",
|
||||
"app_passwd": "Heslo aplikácie",
|
||||
"app_passwd_protocols": "Povolené protokoly pre heslá aplikácií",
|
||||
"app_passwd_protocols": "Povolené protokoly",
|
||||
"automap": "Skúsiť automapovať priečinky (\"Sent items\", \"Sent\" => \"Sent\" atd.)",
|
||||
"backup_mx_options": "Možnosti preposielania",
|
||||
"bcc_dest_format": "Cieľ kópie musí byť jedna platná emailová adresa. Pokiaľ potrebujete posielať kópie na viac adries, vytvorte Alias a použite ho tu.",
|
||||
@@ -614,8 +622,8 @@
|
||||
"sieve_desc": "Krátky popis",
|
||||
"sieve_type": "Typ filtru",
|
||||
"skipcrossduplicates": "Preskočiť duplikované správy naprieč priečinkami (akceptuje sa prvý nález)",
|
||||
"sogo_access": "Udeliť priamy prístup k prihláseniu do služby SOGo",
|
||||
"sogo_access_info": "Jednotné prihlásenie (SSO) z mail UI zostáva funkčné. Toto nastavenie nemá vplyv na prístup k všetkým ostatným službám, ani neodstraňuje alebo nemení existujúci profil používateľa SOGo.",
|
||||
"sogo_access": "Prideliť priame prihlásenie do SOGo",
|
||||
"sogo_access_info": "Jednotné prihlásenie z používateľského mail rozhrania zostáva funkčné. Toto nastavenie nemá vplyv na prístup k ostatným službám, ani neodstraňuje alebo nemení existujúci profil používateľa SOGo.",
|
||||
"sogo_visible": "Alias je viditeľný v SOGo",
|
||||
"sogo_visible_info": "Táto voľba ovplyvňuje len objekty, ktoré dokážu byť zobrazené v SOGo (zdieľané alebo nezdieľané alias adresy ukazujúc na minimálne jednu lokálnu mailovú schránku). Ak je skrytý, alias nebude prezentovaný ako voliteľný odosielateľ v SOGo.",
|
||||
"spam_alias": "Vytvoriť alebo zmeniť časovo limitované alias adresy",
|
||||
@@ -631,10 +639,7 @@
|
||||
"title": "Upraviť objekt",
|
||||
"unchanged_if_empty": "Ak nemeníte, nechajte prázdne",
|
||||
"username": "Používateľské meno",
|
||||
"validate_save": "Validovať a uložiť",
|
||||
"sogo_access": "Prideliť priame prihlásenie do SOGo",
|
||||
"sogo_access_info": "Jednotné prihlásenie z používateľského mail rozhrania zostáva funkčné. Toto nastavenie nemá vplyv na prístup k ostatným službám, ani neodstraňuje alebo nemení existujúci profil používateľa SOGo.",
|
||||
"app_passwd_protocols": "Povolené protokoly"
|
||||
"validate_save": "Validovať a uložiť"
|
||||
},
|
||||
"fido2": {
|
||||
"confirm": "Potvrdiť",
|
||||
@@ -671,6 +676,7 @@
|
||||
"apps": "Aplikácie",
|
||||
"debug": "Systémové informácie",
|
||||
"email": "E-Mail",
|
||||
"mailcow_system": "Systém",
|
||||
"mailcow_config": "Konfigurácia",
|
||||
"quarantine": "Karanténa",
|
||||
"restart_netfilter": "Reštartovať netfilter",
|
||||
@@ -706,6 +712,7 @@
|
||||
"add_mailbox": "Pridať mailovú schránku",
|
||||
"add_recipient_map_entry": "Pridať mapu príjemcu",
|
||||
"add_resource": "Pridať zdroj",
|
||||
"add_template": "Pridať šablónu",
|
||||
"add_tls_policy_map": "Pridať TLS mapu pravidiel",
|
||||
"address_rewriting": "Prepisovanie adries",
|
||||
"alias": "Alias",
|
||||
@@ -748,6 +755,7 @@
|
||||
"domain": "Doména",
|
||||
"domain_admins": "Administrátori domény",
|
||||
"domain_aliases": "Alias domény",
|
||||
"domain_templates": "Šablóny domén",
|
||||
"domain_quota": "Kvóta",
|
||||
"domain_quota_total": "Celkové kvóta domény",
|
||||
"domains": "Domény",
|
||||
@@ -776,6 +784,7 @@
|
||||
"mailbox_defaults": "Predvolené nastavenia",
|
||||
"mailbox_defaults_info": "Definuje predvolené nastavenia pre nové schránky.",
|
||||
"mailbox_defquota": "Predvolená veľkosť schránky",
|
||||
"mailbox_templates": "Šablóny schránok",
|
||||
"mailbox_quota": "Max. veľkosť schránky",
|
||||
"mailboxes": "Mailové schránky",
|
||||
"max_aliases": "Max. počet aliasov",
|
||||
@@ -843,6 +852,8 @@
|
||||
"table_size_show_n": "Zobraziť %s položiek",
|
||||
"target_address": "Cieľová adresa",
|
||||
"target_domain": "Cieľová doména",
|
||||
"templates": "Šablóny",
|
||||
"template": "Šablóna",
|
||||
"tls_enforce_in": "Vynútiť TLS pre prichádzajúcu poštu",
|
||||
"tls_enforce_out": "Vynútiť TLS pre odchádzajúcu poštu",
|
||||
"tls_map_dest": "Cieľ",
|
||||
@@ -1007,6 +1018,9 @@
|
||||
"settings_map_added": "Pridaná mapa nastavení",
|
||||
"settings_map_removed": "Odstránená mapa nastavení ID %s",
|
||||
"sogo_profile_reset": "SOGo profil pre používateľa %s resetovaný",
|
||||
"template_added": "Pridaná šablóna %s",
|
||||
"template_modified": "Zmeny šablóny %s boli uložené",
|
||||
"template_removed": "Šablóna ID %s bola odstránená",
|
||||
"tls_policy_map_entry_deleted": "Položka mapy TLS pravidiel %s vymazaná",
|
||||
"tls_policy_map_entry_saved": "Položka mapy TLS pravidiel \"%s\" uložená",
|
||||
"ui_texts": "Zmeny v UI textoch uložené",
|
||||
@@ -1065,9 +1079,9 @@
|
||||
"apple_connection_profile": "Apple konfiguračný profil",
|
||||
"apple_connection_profile_complete": "Tento profil zahŕňa IMAP a SMTP parametre, ako aj CalDAV (kalendáre) a CardDAV (kontakty) pre zariadenia Apple.",
|
||||
"apple_connection_profile_mailonly": "Tento profil zahŕňa IMAP a SMTP konfiguračné parametre pre zariadenia Apple.",
|
||||
"apple_connection_profile_with_app_password": "Nové heslo aplikácie sa vygeneruje a pridá do profilu, takže pri nastavovaní zariadenia nie je potrebné zadávať žiadne heslo. Súbor nezdieľajte, pretože poskytuje úplný prístup k vašej poštovej schránke.",
|
||||
"apple_connection_profile_with_app_password": "Nové heslo aplikácie sa vygeneruje a pridá do profilu, takže pri nastavovaní zariadenia nie je potrebné zadávať žiadne heslo. Súbor nezdieľajte, pretože poskytuje úplný prístup k vašej mail schránke.",
|
||||
"change_password": "Zmeniť heslo",
|
||||
"change_password_hint_app_passwords": "Váš účet má %d hesiel aplikácií, ktoré nebudú zmenené. Ak ich chcete spravovať, prejdite na kartu Heslá aplikácií.",
|
||||
"change_password_hint_app_passwords": "Vaše konto má %d hesiel aplikácií, ktoré nebudú zmenené. Ak ich chcete spravovať, prejdite na kartu Heslá aplikácií.",
|
||||
"clear_recent_successful_connections": "Vymazať nedávne úspešné prihlásenia",
|
||||
"client_configuration": "Zobraziť konfiguračné pokyny pre emailových klientov a smartfóny",
|
||||
"create_app_passwd": "Vytvoriť heslo aplikácie",
|
||||
@@ -1078,7 +1092,7 @@
|
||||
"delete_ays": "Potvrďte zmazanie.",
|
||||
"direct_aliases": "Priame alias adresy",
|
||||
"direct_aliases_desc": "Priame aliasy sú ovplyvnené spam filtrom a nastavením TLS pravidiel.",
|
||||
"direct_protocol_access": "Tento používateľ mailovej schránky má <b>priamy, externý prístup</b> k nasledujúcim protokolom a aplikáciám. Toto nastavenie má pod kontrolou Váš správca. Na udelenie prístupu k jednotlivým protokolom a aplikáciám je možné vytvoriť heslá aplikácií.<br>Tlačidlo \" Prihláste sa do webmailu\" poskytuje jednotné prihlásenie do systému SOGo a je vždy k dispozícii.",
|
||||
"direct_protocol_access": "Tento používateľ mailovej schránky má <b>priamy, externý prístup</b> k nasledujúcim protokolom a aplikáciám. Toto nastavenie kontroluje administrátor. Na udelenie prístupu k jednotlivým protokolom a aplikáciám je možné vytvoriť heslá aplikácií.<br>Tlačidlo \"Prihlásenie do webmailu\" poskytuje jednotné prihlásenie do systému SOGo a je vždy k dispozícii.",
|
||||
"eas_reset": "Resetovať medzipamäť u ActiveSync zariadení",
|
||||
"eas_reset_help": "Vo väčšine prípadov, reset medzipamäte ActiveSync pomôže opravit nefunkčný profil.<br><b>Pozor:</b> Všetky potrebné dáta budú opäť stiahnuté!",
|
||||
"eas_reset_now": "Reset ActiveSync",
|
||||
@@ -1202,10 +1216,7 @@
|
||||
"weeks": "týždne",
|
||||
"with_app_password": "s heslom aplikácie",
|
||||
"year": "rok",
|
||||
"years": "rokov",
|
||||
"apple_connection_profile_with_app_password": "Nové heslo aplikácie sa vygeneruje a pridá do profilu, takže pri nastavovaní zariadenia nie je potrebné zadávať žiadne heslo. Súbor nezdieľajte, pretože poskytuje úplný prístup k vašej mail schránke.",
|
||||
"change_password_hint_app_passwords": "Vaše konto má %d hesiel aplikácií, ktoré nebudú zmenené. Ak ich chcete spravovať, prejdite na kartu Heslá aplikácií.",
|
||||
"direct_protocol_access": "Tento používateľ mailovej schránky má <b>priamy, externý prístup</b> k nasledujúcim protokolom a aplikáciám. Toto nastavenie kontroluje administrátor. Na udelenie prístupu k jednotlivým protokolom a aplikáciám je možné vytvoriť heslá aplikácií.<br>Tlačidlo \"Prihlásenie do webmailu\" poskytuje jednotné prihlásenie do systému SOGo a je vždy k dispozícii."
|
||||
"years": "rokov"
|
||||
},
|
||||
"warning": {
|
||||
"cannot_delete_self": "Nemožno vymazať prihláseného používateľa",
|
||||
|
||||
@@ -1,61 +1,113 @@
|
||||
{
|
||||
"acl": {
|
||||
"alias_domains": "Takma alan adı ekle",
|
||||
"alias_domains": "Alias domain ekle",
|
||||
"app_passwds": "Uygulama şifrelerini yönet",
|
||||
"delimiter_action": "Sınırlama işlemi",
|
||||
"domain_relayhost": "Bir alan adı için relayhost sunucusunu değiştir",
|
||||
"eas_reset": "EAS cihazlarını sıfırla",
|
||||
"mailbox_relayhost": "Bir posta kutusunun relayhost sunucularını değiştir",
|
||||
"pushover": "Bildirim",
|
||||
"quarantine": "Karantina işlemleri",
|
||||
"quarantine_attachments": "Ekleri karantinaya al",
|
||||
"quarantine_notification": "Karantina bildirimlerini değiştir",
|
||||
"smtp_ip_access": "SMTP sunucularının değiştirilmesine izin ver",
|
||||
"sogo_access": "SOGo erişiminin yönetilmesine izin ver",
|
||||
"bcc_maps": "BCC haritası",
|
||||
"delimiter_action": "Sınırlandırma işlemi",
|
||||
"domain_desc": "Alan adı açıklamasını değiştir",
|
||||
"extend_sender_acl": "Gönderenin acl'sini harici adreslere göre genişletmeye izin ver",
|
||||
"spam_policy": "Engellenenler / İzin verilenler",
|
||||
"filters": "Fitreler"
|
||||
"domain_relayhost": "Alan adı için relayhost sunucusunu değiştir",
|
||||
"eas_reset": "EAS cihazlarını sıfırla",
|
||||
"extend_sender_acl": "Gönderen ACL'sini harici adreslerle genişletmeye izin ver",
|
||||
"filters": "Filtreler",
|
||||
"login_as": "E-posta kullanıcısı olarak giriş yapın",
|
||||
"mailbox_relayhost": "Bir e-posta için relayhost'u değiştirin",
|
||||
"prohibited": "ACL tarafından yasaklandı",
|
||||
"protocol_access": "Protokol erişimini değiştirin",
|
||||
"pushover": "Pushover",
|
||||
"quarantine": "Karantina eylemleri",
|
||||
"quarantine_attachments": "Ekleri karantinaya al",
|
||||
"quarantine_category": "Karantina bildirim kategorisini değiştir",
|
||||
"quarantine_notification": "Karantina bildirimlerini değiştir",
|
||||
"ratelimit": "Rate limit",
|
||||
"recipient_maps": "Alıcı haritaları",
|
||||
"smtp_ip_access": "SMTP için izin verilen host değerlerini değiştirme",
|
||||
"sogo_access": "SOGo erişiminin yönetilmesine izin verin",
|
||||
"sogo_profile_reset": "SOGo profilini sıfırla",
|
||||
"spam_alias": "Geçici alias değerleri",
|
||||
"spam_policy": "Kara Liste/Beyaz Liste",
|
||||
"spam_score": "Spam skoru",
|
||||
"syncjobs": "Görevleri senkronize et",
|
||||
"tls_policy": "TLS ilkesi",
|
||||
"unlimited_quota": "E-postalar için sınırsız kota"
|
||||
},
|
||||
"add": {
|
||||
"activate_filter_warn": "Aktif edilirse diğer tüm filtreler devre dışı bırakılacak.",
|
||||
"active": "Aktif",
|
||||
"add": "Ekle",
|
||||
"add_domain_only": "Sadece alan adı ekle",
|
||||
"alias_address": "Takma ad adres(leri)",
|
||||
"add_domain_restart": "Alan adını ekleyin ve SOGo'yu yeniden başlatın",
|
||||
"alias_address": "Takma adres",
|
||||
"alias_address_info": "<small>Bir alan adına ilişkin tüm iletileri yakalamak için tam e-posta adresi veya @example.com olacak şeklinde girin (virgülle ayırın).<b>sadece mailcow alan adları</b>.</small>",
|
||||
"alias_domain": "Takma alan adı",
|
||||
"alias_domain_info": "<small>Sadece geçerli alan adları (virgülle ayırın).</small>",
|
||||
"backup_mx_options": "İletme ayarları",
|
||||
"delete2": "Kaynakta olmayan hedefteki mesajları sil",
|
||||
"alias_domain_info": "<small>Sadece geçerli alan adları (virgülle ayrılmış).</small>",
|
||||
"app_name": "Uygulama adı",
|
||||
"app_password": "Uygulama şifresi ekle",
|
||||
"app_passwd_protocols": "Uygulama şifresi için izin verilen protokoller",
|
||||
"automap": "Klasörleri otomatik eşleştirmeyi deneyin (\"Gönderilen postalar\", \"Gönderilen\" => \"Gönderilen\" vb.)",
|
||||
"backup_mx_options": "Relay ayarları",
|
||||
"bcc_dest_format": "BCC hedefi tek bir geçerli e-posta adresi olmalıdır.<br>Bir kopyayı birden fazla adrese göndermeniz gerekiyorsa, bir takma ad oluşturun ve bunu burada kullanın.",
|
||||
"comment_info": "Özel bir yorum kullanıcı tarafından görülemezken, herkese açık bir yorum kullanıcının genel görünümünde üzerine gelindiğinde tooltip olarak gösterilir",
|
||||
"custom_params": "Özel parametreler",
|
||||
"custom_params_hint": "Doğru: --param=xy, yanlış: --param xy",
|
||||
"delete1": "Tamamlandığında kaynaktan sil",
|
||||
"delete2": "Eğer kaynakta yoksa hedefteki mesajları sil",
|
||||
"delete2duplicates": "Hedefteki kopyaları sil",
|
||||
"disable_login": "Giriş yapmaya izin verme ( Gelen mailler yine de kabul edilir)",
|
||||
"description": "Açıklama",
|
||||
"destination": "Hedef",
|
||||
"disable_login": "Giriş yapmaya izin verme (gelen mailler yine de kabul edilir)",
|
||||
"domain": "Alan adı",
|
||||
"domain_matches_hostname": "Alan adı %s ana bilgisayar adıyla eşleşiyor",
|
||||
"add_domain_restart": "Alan adı ekleyin ve SOGo'yu yeniden başlatın",
|
||||
"alias_address_info": "<small>Bir alan adına ilişkin tüm iletileri yakalamak için tam e-posta adresi veya @example.com olacak şeklinde girin (virgülle ayırın).<b>sadece mailcow alan adları</b>.</small>",
|
||||
"domain_quota_m": "Toplam alan adı kotası (MiB)",
|
||||
"enc_method": "Şifreleme yöntemi",
|
||||
"exclude": "Hariç tutma kuralı (regex)",
|
||||
"full_name": "Tam isim",
|
||||
"gal": "Global Adres Listesi",
|
||||
"gal_info": "GAL bir alan alanının tüm nesnelerini içerir ve herhangi bir kullanıcı tarafından düzenlenemez. Eğer devre dışı bırakırsanız SOGo üzerindeki free/busy bilgileri kaybolur! <b>Değişiklikleri uygulamak için SOGo'yu yeniden başlatın.</b>",
|
||||
"generate": "oluştur",
|
||||
"goto_ham": "Ham olarak<span class=\"text-success\"><b>işaretle</b></span>",
|
||||
"goto_null": "Postaları sessizce çöpe at",
|
||||
"goto_null": "Postaları çöpe at",
|
||||
"goto_spam": "Spam olarak<span class=\"text-danger\"><b>işaretle</b></span>",
|
||||
"hostname": "Ana sunucu",
|
||||
"inactive": "İnaktif",
|
||||
"kind": "Tür",
|
||||
"mailbox_quota_m": "Posta kutusu başına maksimum kota (MiB)",
|
||||
"max_aliases": "Maksimum olası takma adı",
|
||||
"max_mailboxes": "Maksimum olası posta kutusu",
|
||||
"nexthop": "Sonraki atlama",
|
||||
"mailbox_quota_def": "Varsayılan e-posta kotası",
|
||||
"mailbox_quota_m": "E-posta başına maksimum kota (MiB)",
|
||||
"mailbox_username": "Kullanıcı adı (e-posta adresinin sol kısmı)",
|
||||
"max_aliases": "Maksimum takma adı limiti",
|
||||
"max_mailboxes": "Maksimum e-posta hesabı",
|
||||
"mins_interval": "Sorgulama döngüsü (dakika)",
|
||||
"multiple_bookings": "Birden fazla rezervasyon",
|
||||
"nexthop": "Next hop",
|
||||
"password": "Şifre",
|
||||
"password_repeat": "Şifre (tekrar)",
|
||||
"port": "Port",
|
||||
"public_comment": "Genel yorum",
|
||||
"relay_all": "Tüm alıcılara ilet",
|
||||
"relay_all_info": "Eğer <b>hiçbir</b> alıcıya iletilmemesini seçerseniz, aktarılması gereken her alıcı için bir (\"kör\") posta kutusu eklemeniz gerekecektir.",
|
||||
"relay_domain": "Bu alan adını ilet",
|
||||
"relay_transport_info": "<div class=\"label label-info\">Bilgi</div> Bu etki alanı için özel bir hedef için aktarım eşlemeleri tanımlayabilirsiniz. Ayarlanmazsa, bir MX araması yapılacaktır.",
|
||||
"relay_unknown_only": "Yalnızca mevcut olmayan posta kutularını ilet. Mevcut posta kutuları yerel olarak teslim edilecektir.",
|
||||
"relayhost_wrapped_tls_info": "Lütfen TLS ile örtülmüş portları <b> kullanmayın</b> (çoğu 465 portunda çalışır).<br>\nÖrtülmemiş port kullan ve STARTTLS üzerinden yayınla. TLS'yi zorlamak için bir TLS ilkesi \"TLS ilke eşlemeleri\" sayfası içinde oluşturulabilir.",
|
||||
"skipcrossduplicates": "Klasörler arasında yinelenen mesajları atlayın (ilk mesaj seçilir)",
|
||||
"post_domain_add": "SOGo konteynerinin \"sogo-mailcow\" yeni bir alan adı eklendikten sonra yeniden başlatılması gerekiyor!<br><br>Ayrıca alan adlarının DNS yapılandırması da gözden geçirilmelidir. DNS yapılandırması onaylandıktan sonra, yeni etki alanınız için otomatik olarak sertifika oluşturmak üzere \"acme-mailcow\"u yeniden başlatın (autoconfig.<domain>, autodiscover.<domain>).<br>Bu adım isteğe bağlıdır ve her 24 saatte bir yeniden denenecektir.",
|
||||
"private_comment": "Özel not",
|
||||
"public_comment": "Herkese açık not",
|
||||
"quota_mb": "Kota (MiB)",
|
||||
"relay_all": "Tüm alıcıları aktar",
|
||||
"relay_all_info": "↪ Tüm alıcıları <b>aktarmamayı</b> seçerseniz, aktarılması gereken her alıcı için bir (\"kör\") posta kutusu eklemeniz gerekecektir.",
|
||||
"relay_domain": "Bu etki alanını aktarın",
|
||||
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Bilgi</div> Bu alan adı nezdinde özel bir hedef için transport haritası tanımlayabilirsiniz. Eğer ayarlanmazsa, MX taraması yapılacaktır.",
|
||||
"relay_unknown_only": "Yalnızca mevcut olmayan e-postaları aktarın. Mevcut e-postalar local olarak teslim edilecektir.",
|
||||
"relayhost_wrapped_tls_info": "Lütfen örtülü TLS portları <b>kullanmayın</b> (çoğunlukla 465 portunda kullanılır).<br>\r\nÖrtülmüş olmayan herhangi bir bağlantı noktası kullanın ve STARTTLS üzerinden yayınlayın. TLS'yi zorlamak için bir TLS ilkesi \"TLS ilke eşlemeleri\" sayfası içinde oluşturulabilir.",
|
||||
"select": "Lütfen seçiniz...",
|
||||
"select_domain": "Lütfen önce alan adı seçin",
|
||||
"sieve_desc": "Kısa açıklama",
|
||||
"sieve_type": "Filtre türü",
|
||||
"skipcrossduplicates": "Klasörler arasında yinelenen(kopya) mesajları es geçin (ilk gelen mail baz alınır)",
|
||||
"subscribeall": "Tüm klasörlere abone ol",
|
||||
"syncjob": "Senkronizasyon görevi ekle",
|
||||
"syncjob_hint": "Parolaların düz metin olarak kaydedilmesi gerektiğini unutmayın!",
|
||||
"tags": "Etiketler",
|
||||
"target_address": "Adreslere git",
|
||||
"target_address_info": "<small>Tam e-posta adres(leri) girin ( virgülle ayırın).</small>",
|
||||
"target_address_info": "<small>Tam e-posta adresleri (virgülle ayrılmış).</small>",
|
||||
"target_domain": "Hedef alan adı",
|
||||
"timeout1": "Uzak ana bilgisayara bağlantısı zaman aşımına uğradı",
|
||||
"timeout2": "Yerel ana bilgisayara bağlantı zaman aşımına uğradı"
|
||||
"timeout1": "Uzak ana bilgisayara bağlantı için zaman aşımı",
|
||||
"timeout2": "Yerel ana bilgisayara bağlantı için zaman aşımı",
|
||||
"username": "Kullanıcı Adı",
|
||||
"validate": "Doğrula",
|
||||
"validation_success": "Doğrulama başarılı"
|
||||
},
|
||||
"admin": {
|
||||
"action": "İşlem",
|
||||
@@ -82,5 +134,18 @@
|
||||
"f2b_ban_time": "Yasaklama süresi (saniye)",
|
||||
"f2b_max_attempts": "Maksimum giriş denemesi",
|
||||
"f2b_retry_window": "Maksimum girişim için deneme pencere(leri)"
|
||||
},
|
||||
"warning": {
|
||||
"cannot_delete_self": "Cannot delete logged in user",
|
||||
"domain_added_sogo_failed": "Alan adı eklendi ancak SOGo yeniden başlatılamadı, lütfen sunucu log kayıtlarını kontrol edin.",
|
||||
"dovecot_restart_failed": "Dovecot yeniden başlatılamadı, lütfen log kayıtlarını kontrol edin",
|
||||
"fuzzy_learn_error": "Fuzzy hash hatayı öğrendi: %s",
|
||||
"hash_not_found": "Hash bulunamadı veya zaten silinmiş",
|
||||
"ip_invalid": "Geçersiz IP atlandı: %s",
|
||||
"is_not_primary_alias": "Birincil olmayan alias %s atlandı",
|
||||
"no_active_admin": "Son etkin yönetici devre dışı bırakılamaz",
|
||||
"quota_exceeded_scope": "Domain kotası aşıldı: Bu domain kapsamında yalnızca sınırsız e-posta oluşturulabilir!",
|
||||
"session_token": "Form token geçersiz: Token uyuşmadı",
|
||||
"session_ua": "Form token geçersiz: User-Agent doğrulama hatası"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,8 @@
|
||||
"kind": "Вид",
|
||||
"delete1": "Видалити з джерела після завершення",
|
||||
"delete2duplicates": "Видалити дублікати на місці призначення",
|
||||
"domain_quota_m": "Загальна квота домену (МіБ)"
|
||||
"domain_quota_m": "Загальна квота домену (МіБ)",
|
||||
"dry": "Імітувати синхронізацію"
|
||||
},
|
||||
"admin": {
|
||||
"access": "Налаштування доступу",
|
||||
@@ -650,10 +651,13 @@
|
||||
"auth_user": "{= auth_user =} - Аутентифіковане ім'я користувача, вказане MTA",
|
||||
"from_user": "{= from_user =} - З користувацької частини envelope, наприклад, для \"moo@mailcow.tld\" повертає \"moo\"",
|
||||
"from_addr": "{= from_addr =} - З адресної частини envelope",
|
||||
"from_domain": "{= from_domain =} - З доменної частини envelope"
|
||||
"from_domain": "{= from_domain =} - З доменної частини envelope",
|
||||
"custom": "{= foo =} - Якщо поштова скринька має кастомний атрибут \"foo\" зі значенням \"bar\", то повертається \"bar\""
|
||||
},
|
||||
"domain_footer_html": "Нижній колонтитул HTML",
|
||||
"domain_footer_plain": "ЗВИЧАЙНИЙ нижній колонтитул"
|
||||
"domain_footer_plain": "ЗВИЧАЙНИЙ нижній колонтитул",
|
||||
"custom_attributes": "Користувацькі атрибути",
|
||||
"mbox_exclude": "Виключити поштові скриньки"
|
||||
},
|
||||
"fido2": {
|
||||
"confirm": "Підтвердити",
|
||||
@@ -1248,7 +1252,9 @@
|
||||
"tls_policy_warning": "<strong>Попередження:</strong> якщо ви увімкнете примусове шифрування пошти, ви можете зіткнутися з втратою листів.<br>Повідомлення, які не відповідають політиці, будуть відкидатися з повідомленням поштовим сервером про серйозний збій.<br>Цей параметр застосовується до вашої основної адреси електронної пошти (логіну), усім особистим псевдонімам та псевдонімам доменів. Маються на увазі лише псевдоніми <b>з однією поштовою скринькою</b>, як одержувач.",
|
||||
"year": "рік",
|
||||
"years": "років",
|
||||
"pushover_sound": "Звук"
|
||||
"pushover_sound": "Звук",
|
||||
"value": "Значення",
|
||||
"attribute": "Атрибут"
|
||||
},
|
||||
"warning": {
|
||||
"domain_added_sogo_failed": "Домен був доданий, але перезавантажити SOGo не вдалося, будь ласка, перевірте журнали сервера.",
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
<script type='text/javascript'>
|
||||
var lang_user = {{ lang_user|raw }};
|
||||
var lang_admin = {{ lang_admin|raw }};
|
||||
var lang_datatables = {{ lang_datatables|raw }};
|
||||
var csrf_token = '{{ csrf_token }}';
|
||||
var pagination_size = Math.trunc('{{ pagination_size }}');
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
<label class="control-label col-sm-2">{{ lang.edit.ratelimit }}</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input name="rl_value" type="number" value="{{ rl.value }}" autocomplete="off" class="form-control placeholder="{{ lang.ratelimit.disabled }}">
|
||||
<input name="rl_value" type="number" value="{{ rl.value }}" autocomplete="off" class="form-control" placeholder="{{ lang.ratelimit.disabled }}">
|
||||
<select name="rl_frame" class="form-control">
|
||||
{% include 'mailbox/rl-frame.twig' %}
|
||||
</select>
|
||||
@@ -285,23 +285,41 @@
|
||||
{{ lang.edit.domain_footer_info_vars.from_user }}
|
||||
{{ lang.edit.domain_footer_info_vars.from_name }}
|
||||
{{ lang.edit.domain_footer_info_vars.from_addr }}
|
||||
{{ lang.edit.domain_footer_info_vars.from_domain }}</pre>
|
||||
{{ lang.edit.domain_footer_info_vars.from_domain }}
|
||||
{{ lang.edit.domain_footer_info_vars.custom }}</pre>
|
||||
<form class="form-horizontal mt-4" data-id="domain_footer">
|
||||
<div class="row mb-4">
|
||||
<label class="control-label col-sm-2" for="mbox_exclude">{{ lang.edit.mbox_exclude }}</label>
|
||||
<div class="col-sm-10">
|
||||
<select data-live-search="true" data-width="100%" style="width:100%" id="editMboxExclude" name="mbox_exclude" size="10" multiple>
|
||||
{% for mailbox in mailboxes %}
|
||||
<option value="{{ mailbox }}" {% if mailbox in domain_footer.mbox_exclude %}selected{% endif %}>
|
||||
{{ mailbox }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% for alias in aliases %}
|
||||
<option data-subtext="Alias" value="{{ alias }}" {% if alias in domain_footer.mbox_exclude %}selected{% endif %}>
|
||||
{{ alias }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<label class="control-label col-sm-2" for="domain_footer_html">{{ lang.edit.domain_footer_html }}:</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="10" id="domain_footer_html" name="footer_html">{{ domain_footer.html }}</textarea>
|
||||
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="10" id="domain_footer_html" name="html">{{ domain_footer.html }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<label class="control-label col-sm-2" for="domain_footer_plain">{{ lang.edit.domain_footer_plain }}:</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="10" id="domain_footer_plain" name="footer_plain">{{ domain_footer.plain }}</textarea>
|
||||
<textarea spellcheck="false" autocorrect="off" autocapitalize="none" class="form-control" rows="10" id="domain_footer_plain" name="plain">{{ domain_footer.plain }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<button class="btn btn-xs-lg d-block d-sm-inline btn-success" data-action="edit_selected" data-id="domain_footer" data-item="domain_footer" data-api-url='edit/domain-wide-footer' data-api-attr='{"domain":"{{ domain }}"}' href="#">{{ lang.edit.save }}</button>
|
||||
<button class="btn btn-xs-lg d-block d-sm-inline btn-success" data-action="edit_selected" data-id="domain_footer" data-item="{{ domain }}" data-api-url='edit/domain/footer' data-api-attr='{}' href="#">{{ lang.edit.save }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<div id="mailbox-content" class="responsive-tabs">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation" class="nav-item"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#medit">{{ lang.edit.mailbox }}</button></li>
|
||||
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#mattr">{{ lang.edit.custom_attributes }}</button></li>
|
||||
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#mpushover">{{ lang.edit.pushover }}</button></li>
|
||||
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#macl">{{ lang.edit.acl }}</button></li>
|
||||
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#mrl">{{ lang.edit.ratelimit }}</button></li>
|
||||
@@ -275,6 +276,37 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mattr" class="tab-pane fade" role="tabpanel" aria-labelledby="mailbox-attr">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex d-md-none fs-5">
|
||||
<button class="btn flex-grow-1 text-start" data-bs-target="#collapse-tab-mattr" data-bs-toggle="collapse" aria-controls="collapse-tab-mattr">
|
||||
{{ lang.edit.mailbox }} <span class="badge bg-info table-lines"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="collapse-tab-mattr" class="card-body collapse show" data-bs-parent="#mailbox-content">
|
||||
<form class="form-inline" data-id="mbox_attr" role="form" method="post">
|
||||
<table class="table table-condensed" style="white-space: nowrap;" id="mbox_attr_table">
|
||||
<tr>
|
||||
<th>{{ lang.user.attribute }}</th>
|
||||
<th>{{ lang.user.value }}</th>
|
||||
<th style="width:100px;"> </th>
|
||||
</tr>
|
||||
{% for key, val in result.custom_attributes %}
|
||||
<tr>
|
||||
<td><input class="input-sm input-xs-lg form-control" data-id="mbox_attr" type="text" name="attribute" required value="{{ key }}"></td>
|
||||
<td><input class="input-sm input-xs-lg form-control" data-id="mbox_attr" type="text" name="value" required value="{{ val }}"></td>
|
||||
<td><a href="#" role="button" class="btn btn-sm btn-xs-lg btn-secondary h-100 w-100" type="button">{{ lang.admin.remove_row }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<p><div class="btn-group">
|
||||
<button class="btn btn-sm btn-xs-half d-block d-sm-inline btn-success" data-action="edit_selected" data-item="{{ mailbox }}" data-id="mbox_attr" data-api-url='edit/mailbox/custom-attribute' data-api-attr='{}' href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
|
||||
<button class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" type="button" id="add_mbox_attr_row">{{ lang.admin.add_row }}</button>
|
||||
</div></p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mpushover" class="tab-pane fade" role="tabpanel" aria-labelledby="mailbox-pushover">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex d-md-none fs-5">
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<input type="hidden" value="0" name="skipcrossduplicates">
|
||||
<input type="hidden" value="0" name="active">
|
||||
<input type="hidden" value="0" name="subscribeall">
|
||||
<input type="hidden" value="0" name="dry">
|
||||
<div class="row mb-2">
|
||||
<label class="control-label col-sm-2" for="host1">{{ lang.edit.hostname }}</label>
|
||||
<div class="col-sm-10">
|
||||
@@ -95,7 +96,7 @@
|
||||
<div class="row mb-4">
|
||||
<label class="control-label col-sm-2" for="custom_params">{{ lang.add.custom_params }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="custom_params" id="custom_params" value="{{ result.custom_params }}" placeholder="--dry --some-param=xy --other-param=yx">
|
||||
<input type="text" class="form-control" name="custom_params" id="custom_params" value="{{ result.custom_params }}" placeholder="--some-param=xy --other-param=yx">
|
||||
<small class="text-muted">{{ lang.add.custom_params_hint }}</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -141,6 +142,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<div class="form-check">
|
||||
<label><input type="checkbox" class="form-check-input" value="1" name="dry"{% if result.dry == '1' %} checked{% endif %}> {{ lang.add.dry }} (--dry)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<div class="form-check">
|
||||
|
||||
@@ -955,7 +955,7 @@
|
||||
<div class="row mb-4">
|
||||
<label class="control-label col-sm-2 text-sm-end" for="custom_params">{{ lang.add.custom_params }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="custom_params" placeholder="--dry --some-param=xy --other-param=yx">
|
||||
<input type="text" class="form-control" name="custom_params" placeholder="--some-param=xy --other-param=yx">
|
||||
<small class="text-muted">{{ lang.add.custom_params_hint }}</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -994,13 +994,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="row mb-2">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<div class="form-check">
|
||||
<label><input type="checkbox" class="form-check-input" value="1" name="subscribeall" checked> {{ lang.add.subscribeall }} (--subscribeall)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<div class="form-check">
|
||||
<label><input type="checkbox" class="form-check-input" value="1" name="dry"> {{ lang.add.dry }} (--dry)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<div class="form-check">
|
||||
|
||||
@@ -167,6 +167,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<div class="form-check">
|
||||
<label><input type="checkbox" class="form-check-input" value="1" name="dry" checked> {{ lang.add.dry }} (--dry)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<div class="form-check">
|
||||
|
||||
@@ -58,12 +58,11 @@ services:
|
||||
- redis
|
||||
|
||||
clamd-mailcow:
|
||||
image: mailcow/clamd:1.62
|
||||
image: mailcow/clamd:1.63
|
||||
restart: always
|
||||
depends_on:
|
||||
unbound-mailcow:
|
||||
condition: service_healthy
|
||||
restart: true
|
||||
dns:
|
||||
- ${IPV4_NETWORK:-172.22.1}.254
|
||||
environment:
|
||||
@@ -78,7 +77,7 @@ services:
|
||||
- clamd
|
||||
|
||||
rspamd-mailcow:
|
||||
image: mailcow/rspamd:1.92
|
||||
image: mailcow/rspamd:1.94
|
||||
stop_grace_period: 30s
|
||||
depends_on:
|
||||
- dovecot-mailcow
|
||||
@@ -219,7 +218,7 @@ services:
|
||||
- sogo
|
||||
|
||||
dovecot-mailcow:
|
||||
image: mailcow/dovecot:1.25
|
||||
image: mailcow/dovecot:1.26
|
||||
depends_on:
|
||||
- mysql-mailcow
|
||||
dns:
|
||||
@@ -305,7 +304,6 @@ services:
|
||||
condition: service_started
|
||||
unbound-mailcow:
|
||||
condition: service_healthy
|
||||
restart: true
|
||||
volumes:
|
||||
- ./data/hooks/postfix:/hooks:Z
|
||||
- ./data/conf/postfix:/opt/postfix/conf:z
|
||||
@@ -528,7 +526,7 @@ services:
|
||||
- watchdog
|
||||
|
||||
dockerapi-mailcow:
|
||||
image: mailcow/dockerapi:2.05
|
||||
image: mailcow/dockerapi:2.06
|
||||
security_opt:
|
||||
- label=disable
|
||||
restart: always
|
||||
|
||||
@@ -26,10 +26,10 @@ for bin in openssl curl docker git awk sha1sum grep cut; do
|
||||
done
|
||||
|
||||
if docker compose > /dev/null 2>&1; then
|
||||
if docker compose version --short | grep "^2." > /dev/null 2>&1; then
|
||||
if docker compose version --short | grep -e "^2." -e "^v2." > /dev/null 2>&1; then
|
||||
COMPOSE_VERSION=native
|
||||
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[33mFound Docker Compose Plugin (native).\e[0m"
|
||||
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
|
||||
sleep 2
|
||||
echo -e "\e[33mNotice: You´ll have to update this Compose Version via your Package Manager manually!\e[0m"
|
||||
else
|
||||
@@ -41,8 +41,8 @@ elif docker-compose > /dev/null 2>&1; then
|
||||
if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then
|
||||
if docker-compose version --short | grep "^2." > /dev/null 2>&1; then
|
||||
COMPOSE_VERSION=standalone
|
||||
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[33mFound Docker Compose Standalone.\e[0m"
|
||||
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
|
||||
sleep 2
|
||||
echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
|
||||
else
|
||||
@@ -553,4 +553,4 @@ else
|
||||
echo -e "\e[33mCannot determine current git repository version...\e[0m"
|
||||
fi
|
||||
|
||||
detect_bad_asn
|
||||
detect_bad_asn
|
||||
|
||||
122
helper-scripts/generate_caa_record.py
Executable file
122
helper-scripts/generate_caa_record.py
Executable file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python3
|
||||
# Based on github.com/diafygi/acme-tiny, original copyright:
|
||||
# Copyright Daniel Roesler, under MIT license, see LICENSE at github.com/diafygi/acme-tiny
|
||||
import argparse, subprocess, json, os, sys, base64, binascii, time, hashlib, re, copy, textwrap, logging
|
||||
try:
|
||||
from urllib.request import urlopen, Request # Python 3
|
||||
except ImportError: # pragma: no cover
|
||||
from urllib2 import urlopen, Request # Python 2
|
||||
|
||||
DEFAULT_DIRECTORY_URL = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
LOGGER.addHandler(logging.StreamHandler())
|
||||
LOGGER.setLevel(logging.INFO)
|
||||
|
||||
def get_id(account_key, log=LOGGER, directory_url=DEFAULT_DIRECTORY_URL, contact=None):
|
||||
directory, acct_headers, alg, jwk = None, None, None, None # global variables
|
||||
|
||||
# helper functions - base64 encode for jose spec
|
||||
def _b64(b):
|
||||
return base64.urlsafe_b64encode(b).decode('utf8').replace("=", "")
|
||||
|
||||
# helper function - run external commands
|
||||
def _cmd(cmd_list, stdin=None, cmd_input=None, err_msg="Command Line Error"):
|
||||
proc = subprocess.Popen(cmd_list, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = proc.communicate(cmd_input)
|
||||
if proc.returncode != 0:
|
||||
raise IOError("{0}\n{1}".format(err_msg, err))
|
||||
return out
|
||||
|
||||
# helper function - make request and automatically parse json response
|
||||
def _do_request(url, data=None, err_msg="Error", depth=0):
|
||||
try:
|
||||
resp = urlopen(Request(url, data=data, headers={"Content-Type": "application/jose+json", "User-Agent": "acme-tiny"}))
|
||||
resp_data, code, headers = resp.read().decode("utf8"), resp.getcode(), resp.headers
|
||||
except IOError as e:
|
||||
resp_data = e.read().decode("utf8") if hasattr(e, "read") else str(e)
|
||||
code, headers = getattr(e, "code", None), {}
|
||||
try:
|
||||
resp_data = json.loads(resp_data) # try to parse json results
|
||||
except ValueError:
|
||||
pass # ignore json parsing errors
|
||||
if depth < 100 and code == 400 and resp_data['type'] == "urn:ietf:params:acme:error:badNonce":
|
||||
raise IndexError(resp_data) # allow 100 retrys for bad nonces
|
||||
if code not in [200, 201, 204]:
|
||||
raise ValueError("{0}:\nUrl: {1}\nData: {2}\nResponse Code: {3}\nResponse: {4}".format(err_msg, url, data, code, resp_data))
|
||||
return resp_data, code, headers
|
||||
|
||||
# helper function - make signed requests
|
||||
def _send_signed_request(url, payload, err_msg, depth=0):
|
||||
payload64 = "" if payload is None else _b64(json.dumps(payload).encode('utf8'))
|
||||
new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce']
|
||||
protected = {"url": url, "alg": alg, "nonce": new_nonce}
|
||||
protected.update({"jwk": jwk} if acct_headers is None else {"kid": acct_headers['Location']})
|
||||
protected64 = _b64(json.dumps(protected).encode('utf8'))
|
||||
protected_input = "{0}.{1}".format(protected64, payload64).encode('utf8')
|
||||
out = _cmd(["openssl", "dgst", "-sha256", "-sign", account_key], stdin=subprocess.PIPE, cmd_input=protected_input, err_msg="OpenSSL Error")
|
||||
data = json.dumps({"protected": protected64, "payload": payload64, "signature": _b64(out)})
|
||||
try:
|
||||
return _do_request(url, data=data.encode('utf8'), err_msg=err_msg, depth=depth)
|
||||
except IndexError: # retry bad nonces (they raise IndexError)
|
||||
return _send_signed_request(url, payload, err_msg, depth=(depth + 1))
|
||||
|
||||
# helper function - poll until complete
|
||||
def _poll_until_not(url, pending_statuses, err_msg):
|
||||
result, t0 = None, time.time()
|
||||
while result is None or result['status'] in pending_statuses:
|
||||
assert (time.time() - t0 < 3600), "Polling timeout" # 1 hour timeout
|
||||
time.sleep(0 if result is None else 2)
|
||||
result, _, _ = _send_signed_request(url, None, err_msg)
|
||||
return result
|
||||
|
||||
# parse account key to get public key
|
||||
log.info("Parsing account key...")
|
||||
out = _cmd(["openssl", "rsa", "-in", account_key, "-noout", "-text"], err_msg="OpenSSL Error")
|
||||
pub_pattern = r"modulus:[\s]+?00:([a-f0-9\:\s]+?)\npublicExponent: ([0-9]+)"
|
||||
pub_hex, pub_exp = re.search(pub_pattern, out.decode('utf8'), re.MULTILINE|re.DOTALL).groups()
|
||||
pub_exp = "{0:x}".format(int(pub_exp))
|
||||
pub_exp = "0{0}".format(pub_exp) if len(pub_exp) % 2 else pub_exp
|
||||
alg, jwk = "RS256", {
|
||||
"e": _b64(binascii.unhexlify(pub_exp.encode("utf-8"))),
|
||||
"kty": "RSA",
|
||||
"n": _b64(binascii.unhexlify(re.sub(r"(\s|:)", "", pub_hex).encode("utf-8"))),
|
||||
}
|
||||
accountkey_json = json.dumps(jwk, sort_keys=True, separators=(',', ':'))
|
||||
thumbprint = _b64(hashlib.sha256(accountkey_json.encode('utf8')).digest())
|
||||
|
||||
# get the ACME directory of urls
|
||||
log.info("Getting directory...")
|
||||
directory, _, _ = _do_request(directory_url, err_msg="Error getting directory")
|
||||
log.info("Directory found!")
|
||||
|
||||
# create account and get the global key identifier
|
||||
log.info("Registering account...")
|
||||
reg_payload = {"termsOfServiceAgreed": True} if contact is None else {"termsOfServiceAgreed": True, "contact": contact}
|
||||
account, code, acct_headers = _send_signed_request(directory['newAccount'], reg_payload, "Error registering")
|
||||
log.info("Registered!" if code == 201 else "Already registered!")
|
||||
|
||||
return acct_headers['Location']
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=textwrap.dedent("""\
|
||||
Generate a CAA record for Mailcow.
|
||||
|
||||
Example Usage: python mailcow_gencaa.py --account-key data/assets/ssl/acme/account.pem
|
||||
""")
|
||||
)
|
||||
parser.add_argument("--account-key", required=True, help="path to your Let's Encrypt account private key")
|
||||
parser.add_argument("--quiet", action="store_const", const=logging.ERROR, help="suppress output except for errors")
|
||||
parser.add_argument("--directory-url", default=DEFAULT_DIRECTORY_URL, help="certificate authority directory url, default is Let's Encrypt")
|
||||
parser.add_argument("--contact", metavar="CONTACT", default=None, nargs="*", help="Contact details (e.g. mailto:aaa@bbb.com) for your account-key")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
LOGGER.setLevel(args.quiet or LOGGER.level)
|
||||
id = get_id(args.account_key, log=LOGGER, directory_url=args.directory_url, contact=args.contact)
|
||||
print("Use this as your CAA record:")
|
||||
print('issue 128 "letsencrypt.org;accounturi={}"'.format(id))
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main(sys.argv[1:])
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
|
||||
NEXTCLOUD_VERSION=27.1.2
|
||||
NEXTCLOUD_VERSION=27.1.4
|
||||
|
||||
echo -ne "Checking prerequisites..."
|
||||
sleep 1
|
||||
@@ -106,6 +106,10 @@ elif [[ ${NC_UPDATE} == "y" ]]; then
|
||||
exit 1
|
||||
else
|
||||
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "php /web/nextcloud/updater/updater.phar"
|
||||
NC_SUBD=$(docker exec -i -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:get overwritehost)
|
||||
mv ./data/conf/nginx/nextcloud.conf ./data/conf/nginx/nextcloud.conf-$(date +%s).bak
|
||||
cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/
|
||||
sed -i "s/NC_SUBD/${NC_SUBD}/g" ./data/conf/nginx/nextcloud.conf
|
||||
fi
|
||||
|
||||
elif [[ ${NC_INSTALL} == "y" ]]; then
|
||||
|
||||
150
update.sh
150
update.sh
@@ -32,51 +32,44 @@ prefetch_images() {
|
||||
}
|
||||
|
||||
docker_garbage() {
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
IMGS_TO_DELETE=()
|
||||
for container in $(grep -oP "image: \Kmailcow.+" "${SCRIPT_DIR}/docker-compose.yml"); do
|
||||
REPOSITORY=${container/:*}
|
||||
TAG=${container/*:}
|
||||
V_MAIN=${container/*.}
|
||||
V_SUB=${container/*.}
|
||||
EXISTING_TAGS=$(docker images | grep ${REPOSITORY} | awk '{ print $2 }')
|
||||
for existing_tag in ${EXISTING_TAGS[@]}; do
|
||||
V_MAIN_EXISTING=${existing_tag/*.}
|
||||
V_SUB_EXISTING=${existing_tag/*.}
|
||||
# Not an integer
|
||||
[[ ! $V_MAIN_EXISTING =~ ^[0-9]+$ ]] && continue
|
||||
[[ ! $V_SUB_EXISTING =~ ^[0-9]+$ ]] && continue
|
||||
|
||||
if [[ $V_MAIN_EXISTING == "latest" ]]; then
|
||||
echo "Found deprecated label \"latest\" for repository $REPOSITORY, it should be deleted."
|
||||
IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
|
||||
elif [[ $V_MAIN_EXISTING -lt $V_MAIN ]]; then
|
||||
echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted."
|
||||
IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
|
||||
elif [[ $V_SUB_EXISTING -lt $V_SUB ]]; then
|
||||
echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted."
|
||||
IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
|
||||
declare -A IMAGES_INFO
|
||||
COMPOSE_IMAGES=($(grep -oP "image: \Kmailcow.+" "${SCRIPT_DIR}/docker-compose.yml"))
|
||||
|
||||
for existing_image in $(docker images --format "{{.ID}}:{{.Repository}}:{{.Tag}}" | grep 'mailcow/'); do
|
||||
ID=$(echo $existing_image | cut -d ':' -f 1)
|
||||
REPOSITORY=$(echo $existing_image | cut -d ':' -f 2)
|
||||
TAG=$(echo $existing_image | cut -d ':' -f 3)
|
||||
|
||||
if [[ " ${COMPOSE_IMAGES[@]} " =~ " ${REPOSITORY}:${TAG} " ]]; then
|
||||
continue
|
||||
else
|
||||
IMGS_TO_DELETE+=("$ID")
|
||||
IMAGES_INFO["$ID"]="$REPOSITORY:$TAG"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then
|
||||
echo "Run the following command to delete unused image tags:"
|
||||
echo
|
||||
echo " docker rmi ${IMGS_TO_DELETE[*]}"
|
||||
echo
|
||||
if [ ! $FORCE ]; then
|
||||
read -r -p "Do you want to delete old image tags right now? [y/N] " response
|
||||
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
docker rmi ${IMGS_TO_DELETE[*]}
|
||||
echo "The following unused mailcow images were found:"
|
||||
for id in "${IMGS_TO_DELETE[@]}"; do
|
||||
echo " ${IMAGES_INFO[$id]} ($id)"
|
||||
done
|
||||
|
||||
if [ ! $FORCE ]; then
|
||||
read -r -p "Do you want to delete them to free up some space? [y/N] " response
|
||||
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
docker rmi ${IMGS_TO_DELETE[*]}
|
||||
else
|
||||
echo "OK, skipped."
|
||||
fi
|
||||
else
|
||||
echo "OK, skipped."
|
||||
echo "Running in forced mode! Force removing old mailcow images..."
|
||||
docker rmi ${IMGS_TO_DELETE[*]}
|
||||
fi
|
||||
else
|
||||
echo "Running image removal without extra confirmation due to force mode."
|
||||
docker rmi ${IMGS_TO_DELETE[*]}
|
||||
fi
|
||||
echo -e "\e[32mFurther cleanup...\e[0m"
|
||||
echo "If you want to cleanup further garbage collected by Docker, please make sure all containers are up and running before cleaning your system by executing \"docker system prune\""
|
||||
echo -e "\e[32mFurther cleanup...\e[0m"
|
||||
echo "If you want to cleanup further garbage collected by Docker, please make sure all containers are up and running before cleaning your system by executing \"docker system prune\""
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -178,11 +171,11 @@ remove_obsolete_nginx_ports() {
|
||||
detect_docker_compose_command(){
|
||||
if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
|
||||
if docker compose > /dev/null 2>&1; then
|
||||
if docker compose version --short | grep "2." > /dev/null 2>&1; then
|
||||
if docker compose version --short | grep -e "^2." -e "^v2." > /dev/null 2>&1; then
|
||||
DOCKER_COMPOSE_VERSION=native
|
||||
COMPOSE_COMMAND="docker compose"
|
||||
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[33mFound Docker Compose Plugin (native).\e[0m"
|
||||
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
|
||||
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' $SCRIPT_DIR/mailcow.conf
|
||||
sleep 2
|
||||
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
|
||||
@@ -196,8 +189,8 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
|
||||
if docker-compose version --short | grep "^2." > /dev/null 2>&1; then
|
||||
DOCKER_COMPOSE_VERSION=standalone
|
||||
COMPOSE_COMMAND="docker-compose"
|
||||
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[33mFound Docker Compose Standalone.\e[0m"
|
||||
echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
|
||||
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' $SCRIPT_DIR/mailcow.conf
|
||||
sleep 2
|
||||
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"
|
||||
@@ -893,45 +886,54 @@ done
|
||||
# git remote set-url origin https://github.com/mailcow/mailcow-dockerized
|
||||
|
||||
DEFAULT_REPO=https://github.com/mailcow/mailcow-dockerized
|
||||
CURRENT_REPO=$(git remote get-url origin)
|
||||
CURRENT_REPO=$(git config --get remote.origin.url)
|
||||
if [ "$CURRENT_REPO" != "$DEFAULT_REPO" ]; then
|
||||
echo "The Repository currently used is not the default Mailcow Repository."
|
||||
echo "Currently Repository: $CURRENT_REPO"
|
||||
echo "Default Repository: $DEFAULT_REPO"
|
||||
read -r -p "Should it be changed back to default? [y/N] " repo_response
|
||||
if [[ "$repo_response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
git remote set-url origin $DEFAULT_REPO
|
||||
if [ ! $FORCE ]; then
|
||||
read -r -p "Should it be changed back to default? [y/N] " repo_response
|
||||
if [[ "$repo_response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||
git remote set-url origin $DEFAULT_REPO
|
||||
fi
|
||||
else
|
||||
echo "Running in forced mode... setting Repo to default!"
|
||||
git remote set-url origin $DEFAULT_REPO
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "\e[32mCommitting current status...\e[0m"
|
||||
[[ -z "$(git config user.name)" ]] && git config user.name moo
|
||||
[[ -z "$(git config user.email)" ]] && git config user.email moo@cow.moo
|
||||
[[ ! -z $(git ls-files data/conf/rspamd/override.d/worker-controller-password.inc) ]] && git rm data/conf/rspamd/override.d/worker-controller-password.inc
|
||||
git add -u
|
||||
git commit -am "Before update on ${DATE}" > /dev/null
|
||||
echo -e "\e[32mFetching updated code from remote...\e[0m"
|
||||
git fetch origin #${BRANCH}
|
||||
echo -e "\e[32mMerging local with remote code (recursive, strategy: \"${MERGE_STRATEGY:-theirs}\", options: \"patience\"...\e[0m"
|
||||
git config merge.defaultToUpstream true
|
||||
git merge -X${MERGE_STRATEGY:-theirs} -Xpatience -m "After update on ${DATE}"
|
||||
# Need to use a variable to not pass return codes of if checks
|
||||
MERGE_RETURN=$?
|
||||
if [[ ${MERGE_RETURN} == 128 ]]; then
|
||||
echo -e "\e[31m\nOh no, what happened?\n=> You most likely added files to your local mailcow instance that were now added to the official mailcow repository. Please move them to another location before updating mailcow.\e[0m"
|
||||
exit 1
|
||||
elif [[ ${MERGE_RETURN} == 1 ]]; then
|
||||
echo -e "\e[93mPotenial conflict, trying to fix...\e[0m"
|
||||
git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v
|
||||
git add -A
|
||||
git commit -m "After update on ${DATE}" > /dev/null
|
||||
git checkout .
|
||||
echo -e "\e[32mRemoved and recreated files if necessary.\e[0m"
|
||||
elif [[ ${MERGE_RETURN} != 0 ]]; then
|
||||
echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
|
||||
echo
|
||||
echo "Run $COMPOSE_COMMAND up -d to restart your stack without updates or try again after fixing the mentioned errors."
|
||||
exit 1
|
||||
if [ ! $DEV ]; then
|
||||
echo -e "\e[32mCommitting current status...\e[0m"
|
||||
[[ -z "$(git config user.name)" ]] && git config user.name moo
|
||||
[[ -z "$(git config user.email)" ]] && git config user.email moo@cow.moo
|
||||
[[ ! -z $(git ls-files data/conf/rspamd/override.d/worker-controller-password.inc) ]] && git rm data/conf/rspamd/override.d/worker-controller-password.inc
|
||||
git add -u
|
||||
git commit -am "Before update on ${DATE}" > /dev/null
|
||||
echo -e "\e[32mFetching updated code from remote...\e[0m"
|
||||
git fetch origin #${BRANCH}
|
||||
echo -e "\e[32mMerging local with remote code (recursive, strategy: \"${MERGE_STRATEGY:-theirs}\", options: \"patience\"...\e[0m"
|
||||
git config merge.defaultToUpstream true
|
||||
git merge -X${MERGE_STRATEGY:-theirs} -Xpatience -m "After update on ${DATE}"
|
||||
# Need to use a variable to not pass return codes of if checks
|
||||
MERGE_RETURN=$?
|
||||
if [[ ${MERGE_RETURN} == 128 ]]; then
|
||||
echo -e "\e[31m\nOh no, what happened?\n=> You most likely added files to your local mailcow instance that were now added to the official mailcow repository. Please move them to another location before updating mailcow.\e[0m"
|
||||
exit 1
|
||||
elif [[ ${MERGE_RETURN} == 1 ]]; then
|
||||
echo -e "\e[93mPotenial conflict, trying to fix...\e[0m"
|
||||
git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v
|
||||
git add -A
|
||||
git commit -m "After update on ${DATE}" > /dev/null
|
||||
git checkout .
|
||||
echo -e "\e[32mRemoved and recreated files if necessary.\e[0m"
|
||||
elif [[ ${MERGE_RETURN} != 0 ]]; then
|
||||
echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
|
||||
echo
|
||||
echo "Run $COMPOSE_COMMAND up -d to restart your stack without updates or try again after fixing the mentioned errors."
|
||||
exit 1
|
||||
fi
|
||||
elif [ $DEV ]; then
|
||||
echo -e "\e[33mDEVELOPER MODE: Not creating a git diff and commiting it to prevent development stuff within a backup diff...\e[0m"
|
||||
fi
|
||||
|
||||
echo -e "\e[32mFetching new images, if any...\e[0m"
|
||||
|
||||
Reference in New Issue
Block a user