1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2026-01-30 00:57:15 +00:00

Merge branch 'staging' into feat/restrict-alias-sending

This commit is contained in:
FreddleSpl0it
2026-01-28 10:02:03 +01:00
committed by GitHub
27 changed files with 352 additions and 151 deletions

View File

@@ -3,7 +3,7 @@ FROM alpine:3.21
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.17
ARG GOSU_VERSION=1.19
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8

View File

@@ -3,15 +3,15 @@ FROM php:8.2-fpm-alpine3.21
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.27
ARG APCU_PECL_VERSION=5.1.28
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
ARG IMAGICK_PECL_VERSION=3.8.0
ARG IMAGICK_PECL_VERSION=3.8.1
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MAILPARSE_PECL_VERSION=3.1.9
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MEMCACHED_PECL_VERSION=3.3.0
ARG MEMCACHED_PECL_VERSION=3.4.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
ARG REDIS_PECL_VERSION=6.2.0
ARG REDIS_PECL_VERSION=6.3.0
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
ARG COMPOSER_VERSION=2.8.6

View File

@@ -6,7 +6,7 @@ ARG DEBIAN_FRONTEND=noninteractive
ARG DEBIAN_VERSION=bookworm
ARG SOGO_DEBIAN_REPOSITORY=https://packagingv2.sogo.nu/sogo-nightly-debian/
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.17
ARG GOSU_VERSION=1.19
ENV LC_ALL=C
# Prerequisites

View File

@@ -80,14 +80,21 @@ if ($isSOGoRequest) {
}
if ($result === false){
// If it's a SOGo Request, don't check for protocol access
$service = ($isSOGoRequest) ? false : array($post['service'] => true);
$result = apppass_login($post['username'], $post['password'], $service, array(
if ($isSOGoRequest) {
$service = 'SOGO';
$post['service'] = 'NONE';
} else {
$service = $post['service'];
}
$result = apppass_login($post['username'], $post['password'], array(
'service' => $post['service'],
'is_internal' => true,
'remote_addr' => $post['real_rip']
));
if ($result) {
error_log('MAILCOWAUTH: App auth for user ' . $post['username'] . " with service " . $post['service'] . " from IP " . $post['real_rip']);
set_sasl_log($post['username'], $post['real_rip'], $post['service']);
error_log('MAILCOWAUTH: App auth for user ' . $post['username'] . " with service " . $service . " from IP " . $post['real_rip']);
set_sasl_log($post['username'], $post['real_rip'], $service);
}
}
if ($result === false){

View File

@@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Mon Dec 1 00:24:43 UTC 2025
# Whitelist generated by Postwhite v3.4 on Thu Jan 1 00:24:01 UTC 2026
# https://github.com/stevejenkins/postwhite/
# 2186 total rules
# 2105 total rules
2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit
2a01:111:f403:2800::/53 permit
@@ -54,8 +54,8 @@
8.36.116.0/24 permit
8.39.144.0/24 permit
12.130.86.238 permit
13.107.213.69 permit
13.107.246.69 permit
13.107.213.38 permit
13.107.246.38 permit
13.108.16.0/20 permit
13.110.208.0/21 permit
13.110.209.0/24 permit
@@ -65,7 +65,6 @@
13.111.191.0/24 permit
13.216.7.111 permit
13.216.54.180 permit
13.247.164.219 permit
15.200.21.50 permit
15.200.44.248 permit
15.200.201.185 permit
@@ -296,14 +295,6 @@
52.94.124.0/28 permit
52.95.48.152/29 permit
52.95.49.88/29 permit
52.96.91.34 permit
52.96.111.82 permit
52.96.172.98 permit
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/15 permit
52.102.0.0/16 permit
52.103.0.0/17 permit
@@ -397,19 +388,8 @@
64.207.219.143 permit
64.233.160.0/19 permit
65.52.80.137 permit
65.54.121.120/29 permit
65.55.29.77 permit
65.55.33.64/28 permit
65.55.42.224/28 permit
65.55.52.224/27 permit
65.55.78.128/25 permit
65.55.81.48/28 permit
65.55.94.0/25 permit
65.55.113.64/26 permit
65.55.126.0/25 permit
65.55.174.0/25 permit
65.55.178.128/27 permit
65.55.234.192/26 permit
65.110.161.77 permit
65.123.29.213 permit
65.123.29.220 permit
@@ -529,7 +509,6 @@
69.169.224.0/20 permit
69.171.232.0/24 permit
69.171.244.0/23 permit
70.37.151.128/25 permit
70.42.149.35 permit
72.3.185.0/24 permit
72.14.192.0/18 permit
@@ -654,12 +633,18 @@
81.169.146.245 permit
81.169.146.246 permit
81.223.46.0/27 permit
82.165.159.2 permit
82.165.159.3 permit
82.165.159.4 permit
82.165.159.12 permit
82.165.159.13 permit
82.165.159.14 permit
82.165.159.34 permit
82.165.159.35 permit
82.165.159.40 permit
82.165.159.41 permit
82.165.159.42 permit
82.165.159.45 permit
82.165.159.130 permit
82.165.159.131 permit
85.9.206.169 permit
@@ -715,8 +700,6 @@
91.198.2.0/24 permit
91.211.240.0/22 permit
94.236.119.0/26 permit
94.245.112.0/27 permit
94.245.112.10/31 permit
95.131.104.0/21 permit
95.217.114.154 permit
96.43.144.0/20 permit
@@ -1354,11 +1337,6 @@
108.179.144.0/20 permit
109.224.244.0/24 permit
109.237.142.0/24 permit
111.221.23.128/25 permit
111.221.26.0/27 permit
111.221.66.0/25 permit
111.221.69.128/25 permit
111.221.112.0/21 permit
112.19.199.64/29 permit
112.19.242.64/29 permit
116.214.12.47 permit
@@ -1420,6 +1398,7 @@
129.153.194.228 permit
129.154.255.129 permit
129.158.56.255 permit
129.158.62.153 permit
129.159.22.159 permit
129.159.87.137 permit
129.213.195.191 permit
@@ -1441,16 +1420,6 @@
134.170.143.0/24 permit
134.170.174.0/24 permit
135.84.216.0/22 permit
136.143.160.0/24 permit
136.143.161.0/24 permit
136.143.162.0/24 permit
136.143.176.0/24 permit
136.143.177.0/24 permit
136.143.178.49 permit
136.143.182.0/23 permit
136.143.184.0/24 permit
136.143.188.0/24 permit
136.143.190.0/23 permit
136.146.128.0/20 permit
136.147.128.0/20 permit
136.147.135.0/24 permit
@@ -1468,6 +1437,8 @@
139.138.58.119 permit
139.180.17.0/24 permit
140.238.148.191 permit
141.148.55.217 permit
141.148.91.244 permit
141.148.159.229 permit
141.193.32.0/23 permit
141.193.184.32/27 permit
@@ -1513,6 +1484,7 @@
149.72.234.184 permit
149.72.248.236 permit
149.97.173.180 permit
150.136.21.199 permit
150.230.98.160 permit
151.145.38.14 permit
152.67.105.195 permit
@@ -1522,17 +1494,7 @@
155.248.220.138 permit
155.248.234.149 permit
155.248.237.141 permit
157.55.9.128/25 permit
157.55.11.0/25 permit
157.55.49.0/25 permit
157.55.61.0/24 permit
157.55.157.128/25 permit
157.55.225.0/25 permit
157.56.24.0/25 permit
157.56.120.128/26 permit
157.56.232.0/21 permit
157.56.240.0/20 permit
157.56.248.0/21 permit
157.58.30.128/25 permit
157.58.196.96/29 permit
157.58.249.3 permit
@@ -1582,6 +1544,9 @@
163.114.135.16 permit
163.116.128.0/17 permit
163.192.116.87 permit
163.192.125.176 permit
163.192.196.146 permit
163.192.204.161 permit
164.152.23.32 permit
164.152.25.241 permit
164.177.132.168/30 permit
@@ -1614,6 +1579,7 @@
168.245.12.252 permit
168.245.46.9 permit
168.245.127.231 permit
170.9.232.254 permit
170.10.128.0/24 permit
170.10.129.0/24 permit
170.10.132.56/29 permit
@@ -1623,7 +1589,6 @@
173.0.84.224/27 permit
173.0.94.244/30 permit
173.194.0.0/16 permit
173.194.0.0/17 permit
173.203.79.182 permit
173.203.81.39 permit
173.224.161.128/25 permit
@@ -1852,7 +1817,6 @@
204.14.232.64/28 permit
204.14.234.64/28 permit
204.75.142.0/24 permit
204.79.197.212 permit
204.92.114.187 permit
204.92.114.203 permit
204.92.114.204/31 permit
@@ -1878,23 +1842,13 @@
206.165.246.80/29 permit
206.191.224.0/19 permit
206.246.157.1 permit
207.46.4.128/25 permit
207.46.22.35 permit
207.46.50.72 permit
207.46.50.82 permit
207.46.50.192/26 permit
207.46.50.224 permit
207.46.52.71 permit
207.46.52.79 permit
207.46.58.128/25 permit
207.46.116.128/29 permit
207.46.132.128/27 permit
207.46.198.0/25 permit
207.46.200.0/27 permit
207.67.38.0/24 permit
207.67.98.192/27 permit
207.68.176.0/26 permit
207.68.176.96/27 permit
207.97.204.96/29 permit
207.126.144.0/20 permit
207.171.160.0/19 permit
@@ -1993,11 +1947,19 @@
212.82.111.228/31 permit
212.82.111.230 permit
212.123.28.40 permit
212.227.15.3 permit
212.227.15.4 permit
212.227.15.5 permit
212.227.15.6 permit
212.227.15.7 permit
212.227.15.8 permit
212.227.15.14 permit
212.227.15.15 permit
212.227.15.18 permit
212.227.15.19 permit
212.227.15.25 permit
212.227.15.26 permit
212.227.15.29 permit
212.227.15.44 permit
212.227.15.45 permit
212.227.15.46 permit
@@ -2005,11 +1967,17 @@
212.227.15.50 permit
212.227.15.52 permit
212.227.15.53 permit
212.227.15.54 permit
212.227.15.55 permit
212.227.17.1 permit
212.227.17.2 permit
212.227.17.7 permit
212.227.17.11 permit
212.227.17.12 permit
212.227.17.16 permit
212.227.17.17 permit
212.227.17.18 permit
212.227.17.19 permit
212.227.17.20 permit
212.227.17.21 permit
212.227.17.22 permit
@@ -2035,8 +2003,6 @@
213.199.128.145 permit
213.199.138.181 permit
213.199.138.191 permit
213.199.161.128/27 permit
213.199.177.0/26 permit
216.17.150.242 permit
216.17.150.251 permit
216.24.224.0/20 permit
@@ -2064,7 +2030,6 @@
216.39.62.60/31 permit
216.39.62.136/29 permit
216.39.62.144/31 permit
216.58.192.0/19 permit
216.66.217.240/29 permit
216.71.138.33 permit
216.71.152.207 permit
@@ -2094,6 +2059,8 @@
216.205.24.0/24 permit
216.221.160.0/19 permit
216.239.32.0/19 permit
217.72.192.77 permit
217.72.192.78 permit
217.77.141.52 permit
217.77.141.59 permit
217.175.194.0/24 permit

View File

@@ -29,8 +29,8 @@ header('Content-Type: application/xml');
<clientConfig version="1.1">
<emailProvider id="<?=$mailcow_hostname; ?>">
<domain>%EMAILDOMAIN%</domain>
<displayName>A mailcow mail server</displayName>
<displayShortName>mail server</displayShortName>
<displayName><?=$autodiscover_config['displayName']; ?></displayName>
<displayShortName><?=$autodiscover_config['displayShortName']; ?></displayShortName>
<incomingServer type="imap">
<hostname><?=$autodiscover_config['imap']['server']; ?></hostname>

View File

@@ -79,7 +79,7 @@ if (empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW'])) {
exit(0);
}
$login_role = check_login($login_user, $login_pass, array('eas' => TRUE));
$login_role = check_login($login_user, $login_pass, array('service' => 'EAS'));
if ($login_role === "user") {
header("Content-Type: application/xml");

View File

@@ -1,10 +1,11 @@
<?php
function check_login($user, $pass, $app_passwd_data = false, $extra = null) {
function check_login($user, $pass, $extra = null) {
global $pdo;
global $redis;
$is_internal = $extra['is_internal'];
$role = $extra['role'];
$extra['service'] = !isset($extra['service']) ? 'NONE' : $extra['service'];
// Try validate admin
if (!isset($role) || $role == "admin") {
@@ -25,34 +26,20 @@ function check_login($user, $pass, $app_passwd_data = false, $extra = null) {
// Try validate app password
if (!isset($role) || $role == "app") {
$result = apppass_login($user, $pass, $app_passwd_data);
$result = apppass_login($user, $pass, $extra);
if ($result !== false) {
if ($app_passwd_data['eas'] === true) {
$service = 'EAS';
} elseif ($app_passwd_data['dav'] === true) {
$service = 'DAV';
} else {
$service = 'NONE';
}
$real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
set_sasl_log($user, $real_rip, $service, $pass);
set_sasl_log($user, $real_rip, $extra['service'], $pass);
return $result;
}
}
// Try validate user
if (!isset($role) || $role == "user") {
$result = user_login($user, $pass);
$result = user_login($user, $pass, $extra);
if ($result !== false) {
if ($app_passwd_data['eas'] === true) {
$service = 'EAS';
} elseif ($app_passwd_data['dav'] === true) {
$service = 'DAV';
} else {
$service = 'MAILCOWUI';
}
$real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
set_sasl_log($user, $real_rip, $service);
set_sasl_log($user, $real_rip, $extra['service']);
return $result;
}
}
@@ -193,7 +180,7 @@ function user_login($user, $pass, $extra = null){
global $iam_settings;
$is_internal = $extra['is_internal'];
$service = $extra['service'];
$extra['service'] = !isset($extra['service']) ? 'NONE' : $extra['service'];
if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
if (!$is_internal){
@@ -236,10 +223,10 @@ function user_login($user, $pass, $extra = null){
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($row)) {
// check if user has access to service (imap, smtp, pop3, sieve) if service is set
// check if user has access to service (imap, smtp, pop3, sieve, dav, eas) if service is set
$row['attributes'] = json_decode($row['attributes'], true);
if (isset($service)) {
$key = strtolower($service) . "_access";
if ($extra['service'] != 'NONE') {
$key = strtolower($extra['service']) . "_access";
if (isset($row['attributes'][$key]) && $row['attributes'][$key] != '1') {
return false;
}
@@ -253,8 +240,8 @@ function user_login($user, $pass, $extra = null){
// check if user has access to service (imap, smtp, pop3, sieve) if service is set
$row['attributes'] = json_decode($row['attributes'], true);
if (isset($service)) {
$key = strtolower($service) . "_access";
if ($extra['service'] != 'NONE') {
$key = strtolower($extra['service']) . "_access";
if (isset($row['attributes'][$key]) && $row['attributes'][$key] != '1') {
return false;
}
@@ -408,7 +395,7 @@ function user_login($user, $pass, $extra = null){
return false;
}
function apppass_login($user, $pass, $app_passwd_data, $extra = null){
function apppass_login($user, $pass, $extra = null){
global $pdo;
$is_internal = $extra['is_internal'];
@@ -424,20 +411,8 @@ function apppass_login($user, $pass, $app_passwd_data, $extra = null){
return false;
}
$protocol = false;
if ($app_passwd_data['eas']){
$protocol = 'eas';
} else if ($app_passwd_data['dav']){
$protocol = 'dav';
} else if ($app_passwd_data['smtp']){
$protocol = 'smtp';
} else if ($app_passwd_data['imap']){
$protocol = 'imap';
} else if ($app_passwd_data['sieve']){
$protocol = 'sieve';
} else if ($app_passwd_data['pop3']){
$protocol = 'pop3';
} else if (!$is_internal) {
$extra['service'] = !isset($extra['service']) ? 'NONE' : $extra['service'];
if (!$is_internal && $extra['service'] == 'NONE') {
return false;
}
@@ -458,7 +433,7 @@ function apppass_login($user, $pass, $app_passwd_data, $extra = null){
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
if ($protocol && $row[$protocol . '_access'] != '1'){
if ($extra['service'] != 'NONE' && $row[strtolower($extra['service']) . '_access'] != '1'){
continue;
}

View File

@@ -205,6 +205,42 @@ function password_complexity($_action, $_data = null) {
break;
}
}
function password_generate(){
$password_complexity = password_complexity('get');
$min_length = max(16, intval($password_complexity['length']));
$lowercase = range('a', 'z');
$uppercase = range('A', 'Z');
$digits = range(0, 9);
$special_chars = str_split('!@#$%^&*()?=');
$password = [
$lowercase[random_int(0, count($lowercase) - 1)],
$uppercase[random_int(0, count($uppercase) - 1)],
$digits[random_int(0, count($digits) - 1)],
$special_chars[random_int(0, count($special_chars) - 1)],
];
$all = array_merge($lowercase, $uppercase, $digits, $special_chars);
while (count($password) < $min_length) {
$password[] = $all[random_int(0, count($all) - 1)];
}
// Cryptographically secure shuffle using Fisher-Yates algorithm
$count = count($password);
for ($i = $count - 1; $i > 0; $i--) {
$j = random_int(0, $i);
$temp = $password[$i];
$password[$i] = $password[$j];
$password[$j] = $temp;
}
return implode('', $password);
}
function password_check($password1, $password2) {
$password_complexity = password_complexity('get');

View File

@@ -1078,6 +1078,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$_data['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
$_data['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
$_data['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
$_data['eas_access'] = (in_array('eas', $_data['protocol_access'])) ? 1 : 0;
$_data['dav_access'] = (in_array('dav', $_data['protocol_access'])) ? 1 : 0;
}
$active = (isset($_data['active'])) ? intval($_data['active']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['active']);
$force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']);
@@ -1088,6 +1090,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$pop3_access = (isset($_data['pop3_access'])) ? intval($_data['pop3_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
$smtp_access = (isset($_data['smtp_access'])) ? intval($_data['smtp_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
$sieve_access = (isset($_data['sieve_access'])) ? intval($_data['sieve_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
$eas_access = (isset($_data['eas_access'])) ? intval($_data['eas_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['eas_access']);
$dav_access = (isset($_data['dav_access'])) ? intval($_data['dav_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['dav_access']);
$relayhost = (isset($_data['relayhost'])) ? intval($_data['relayhost']) : 0;
$quarantine_notification = (isset($_data['quarantine_notification'])) ? strval($_data['quarantine_notification']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
$quarantine_category = (isset($_data['quarantine_category'])) ? strval($_data['quarantine_category']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
@@ -1106,6 +1110,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'pop3_access' => strval($pop3_access),
'smtp_access' => strval($smtp_access),
'sieve_access' => strval($sieve_access),
'eas_access' => strval($eas_access),
'dav_access' => strval($dav_access),
'relayhost' => strval($relayhost),
'passwd_update' => time(),
'mailbox_format' => strval($MAILBOX_DEFAULT_ATTRIBUTES['mailbox_format']),
@@ -1724,12 +1730,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
$attr['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
$attr['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
$attr['eas_access'] = (in_array('eas', $_data['protocol_access'])) ? 1 : 0;
$attr['dav_access'] = (in_array('dav', $_data['protocol_access'])) ? 1 : 0;
}
else {
$attr['imap_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['imap_access']);
$attr['pop3_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['pop3_access']);
$attr['smtp_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['smtp_access']);
$attr['sieve_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['sieve_access']);
$attr['eas_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['eas_access']);
$attr['dav_access'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['dav_access']);
}
if (isset($_data['acl'])) {
$_data['acl'] = (array)$_data['acl'];
@@ -3049,6 +3059,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$_data['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
$_data['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
$_data['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
$_data['eas_access'] = (in_array('eas', $_data['protocol_access'])) ? 1 : 0;
$_data['dav_access'] = (in_array('dav', $_data['protocol_access'])) ? 1 : 0;
}
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
@@ -3058,6 +3070,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
(int)$pop3_access = (isset($_data['pop3_access']) && hasACLAccess("protocol_access")) ? intval($_data['pop3_access']) : intval($is_now['attributes']['pop3_access']);
(int)$smtp_access = (isset($_data['smtp_access']) && hasACLAccess("protocol_access")) ? intval($_data['smtp_access']) : intval($is_now['attributes']['smtp_access']);
(int)$sieve_access = (isset($_data['sieve_access']) && hasACLAccess("protocol_access")) ? intval($_data['sieve_access']) : intval($is_now['attributes']['sieve_access']);
(int)$eas_access = (isset($_data['eas_access']) && hasACLAccess("protocol_access")) ? intval($_data['eas_access']) : intval($is_now['attributes']['eas_access']);
(int)$dav_access = (isset($_data['dav_access']) && hasACLAccess("protocol_access")) ? intval($_data['dav_access']) : intval($is_now['attributes']['dav_access']);
(int)$relayhost = (isset($_data['relayhost']) && hasACLAccess("mailbox_relayhost")) ? intval($_data['relayhost']) : intval($is_now['attributes']['relayhost']);
(int)$quota_m = (isset_has_content($_data['quota'])) ? intval($_data['quota']) : ($is_now['quota'] / 1048576);
$name = (!empty($_data['name'])) ? ltrim(rtrim($_data['name'], '>'), '<') : $is_now['name'];
@@ -3351,6 +3365,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`attributes` = JSON_SET(`attributes`, '$.pop3_access', :pop3_access),
`attributes` = JSON_SET(`attributes`, '$.relayhost', :relayhost),
`attributes` = JSON_SET(`attributes`, '$.smtp_access', :smtp_access),
`attributes` = JSON_SET(`attributes`, '$.eas_access', :eas_access),
`attributes` = JSON_SET(`attributes`, '$.dav_access', :dav_access),
`attributes` = JSON_SET(`attributes`, '$.recovery_email', :recovery_email),
`attributes` = JSON_SET(`attributes`, '$.attribute_hash', :attribute_hash)
WHERE `username` = :username");
@@ -3365,6 +3381,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':pop3_access' => $pop3_access,
':sieve_access' => $sieve_access,
':smtp_access' => $smtp_access,
':eas_access' => $eas_access,
':dav_access' => $dav_access,
':recovery_email' => $pw_recovery_email,
':relayhost' => $relayhost,
':username' => $username,
@@ -3747,6 +3765,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr['pop3_access'] = (in_array('pop3', $_data['protocol_access'])) ? 1 : 0;
$attr['smtp_access'] = (in_array('smtp', $_data['protocol_access'])) ? 1 : 0;
$attr['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0;
$attr['eas_access'] = (in_array('eas', $_data['protocol_access'])) ? 1 : 0;
$attr['dav_access'] = (in_array('dav', $_data['protocol_access'])) ? 1 : 0;
}
else {
foreach ($is_now as $key => $value){

View File

@@ -4,7 +4,7 @@ function init_db_schema()
try {
global $pdo;
$db_version = "16122025_1230";
$db_version = "28012026_1000";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -1395,6 +1395,8 @@ function init_db_schema()
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.imap_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.imap_access') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.pop3_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.pop3_access') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.smtp_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.smtp_access') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.eas_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.eas_access') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.dav_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.dav_access') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.mailbox_format', \"maildir:\") WHERE JSON_VALUE(`attributes`, '$.mailbox_format') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.quarantine_notification', \"never\") WHERE JSON_VALUE(`attributes`, '$.quarantine_notification') IS NULL;");
$pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.quarantine_category', \"reject\") WHERE JSON_VALUE(`attributes`, '$.quarantine_category') IS NULL;");

View File

@@ -121,7 +121,7 @@ class mailcowPdo extends OAuth2\Storage\Pdo {
$this->config['user_table'] = 'mailbox';
}
public function checkUserCredentials($username, $password) {
if (check_login($username, $password) == 'user') {
if (check_login($username, $password, array("role" => "user", "service" => "NONE")) == 'user') {
return true;
}
return false;

View File

@@ -44,7 +44,7 @@ if (isset($_GET["cancel_tfa_login"])) {
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
$login_user = strtolower(trim($_POST["login_user"]));
$as = check_login($login_user, $_POST["pass_user"], false, array("role" => "admin"));
$as = check_login($login_user, $_POST["pass_user"], array("role" => "admin", "service" => "MAILCOWUI"));
if ($as == "admin") {
session_regenerate_id(true);

View File

@@ -55,7 +55,7 @@ if (isset($_GET["cancel_tfa_login"])) {
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
$login_user = strtolower(trim($_POST["login_user"]));
$as = check_login($login_user, $_POST["pass_user"], false, array("role" => "domain_admin"));
$as = check_login($login_user, $_POST["pass_user"], array("role" => "domain_admin", "service" => "MAILCOWUI"));
if ($as == "domainadmin") {
session_regenerate_id(true);

View File

@@ -119,7 +119,7 @@ if (isset($_GET["cancel_tfa_login"])) {
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
$login_user = strtolower(trim($_POST["login_user"]));
$as = check_login($login_user, $_POST["pass_user"], false, array("role" => "user"));
$as = check_login($login_user, $_POST["pass_user"], array("role" => "user", "service" => "MAILCOWUI"));
if ($as == "user") {
set_user_loggedin_session($login_user);

View File

@@ -33,6 +33,8 @@ if ($https_port === FALSE) {
//$https_port = 1234;
// Other settings =>
$autodiscover_config = array(
'displayName' => 'A mailcow mail server',
'displayShortName' => 'mail server',
// General autodiscover service type: "activesync" or "imap"
// emClient uses autodiscover, but does not support ActiveSync. mailcow excludes emClient from ActiveSync.
// With SOGo disabled, the type will always fallback to imap. CalDAV and CardDAV will be excluded, too.
@@ -215,6 +217,12 @@ $MAILBOX_DEFAULT_ATTRIBUTES['smtp_access'] = true;
// Mailbox has sieve access by default
$MAILBOX_DEFAULT_ATTRIBUTES['sieve_access'] = true;
// Mailbox has ActiveSync/EAS access by default
$MAILBOX_DEFAULT_ATTRIBUTES['eas_access'] = true;
// Mailbox has CalDAV/CardDAV (DAV) access by default
$MAILBOX_DEFAULT_ATTRIBUTES['dav_access'] = true;
// Mailbox receives notifications about...
// "add_header" - mail that was put into the Junk folder
// "reject" - mail that was rejected

View File

@@ -27,6 +27,12 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
exit();
}
$host = strtolower($_SERVER['HTTP_HOST'] ?? '');
if (str_starts_with($host, 'autodiscover.') || str_starts_with($host, 'autoconfig.')) {
http_response_code(404);
exit();
}
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php';
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
$_SESSION['index_query_string'] = $_SERVER['QUERY_STRING'];

View File

@@ -352,6 +352,12 @@ $(document).ready(function() {
if (template.sieve_access == 1){
protocol_access.push("sieve");
}
if (template.eas_access == 1){
protocol_access.push("eas");
}
if (template.dav_access == 1){
protocol_access.push("dav");
}
$('#protocol_access').selectpicker('val', protocol_access);
var acl = [];
@@ -933,6 +939,8 @@ jQuery(function($){
item.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
item.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
item.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
item.eas_access = '<i class="text-' + (item.attributes.eas_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.eas_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
item.dav_access = '<i class="text-' + (item.attributes.dav_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.dav_access == 1 ? 'check-lg' : 'x-lg') + '"></i>';
if (item.attributes.quarantine_notification === 'never') {
item.quarantine_notification = lang.never;
} else if (item.attributes.quarantine_notification === 'hourly') {
@@ -1096,6 +1104,18 @@ jQuery(function($){
defaultContent: '',
className: 'none'
},
{
title: 'EAS',
data: 'eas_access',
defaultContent: '',
className: 'none'
},
{
title: 'DAV',
data: 'dav_access',
defaultContent: '',
className: 'none'
},
{
title: lang.quarantine_notification,
data: 'quarantine_notification',
@@ -1209,6 +1229,8 @@ jQuery(function($){
item.attributes.imap_access = '<i class="text-' + (item.attributes.imap_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.imap_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.imap_access == 1 ? '1' : '0') + '</span></i>';
item.attributes.smtp_access = '<i class="text-' + (item.attributes.smtp_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.smtp_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.smtp_access == 1 ? '1' : '0') + '</span></i>';
item.attributes.sieve_access = '<i class="text-' + (item.attributes.sieve_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sieve_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sieve_access == 1 ? '1' : '0') + '</span></i>';
item.attributes.eas_access = '<i class="text-' + (item.attributes.eas_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.eas_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.eas_access == 1 ? '1' : '0') + '</span></i>';
item.attributes.dav_access = '<i class="text-' + (item.attributes.dav_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.dav_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.dav_access == 1 ? '1' : '0') + '</span></i>';
item.attributes.sogo_access = '<i class="text-' + (item.attributes.sogo_access == 1 ? 'success' : 'danger') + ' bi bi-' + (item.attributes.sogo_access == 1 ? 'check-lg' : 'x-lg') + '"><span class="sorting-value">' + (item.attributes.sogo_access == 1 ? '1' : '0') + '</span></i>';
if (item.attributes.quarantine_notification === 'never') {
item.attributes.quarantine_notification = lang.never;
@@ -1317,6 +1339,16 @@ jQuery(function($){
data: 'attributes.sieve_access',
defaultContent: '',
},
{
title: 'EAS',
data: 'attributes.eas_access',
defaultContent: '',
},
{
title: 'DAV',
data: 'attributes.dav_access',
defaultContent: '',
},
{
title: 'SOGO',
data: 'attributes.sogo_access',

View File

@@ -295,7 +295,9 @@
"user_quicklink": "Gyorshivatkozás elrejtése a Felhasználói bejelentkezési oldalra",
"validate_license_now": "GUID érvényesítése a licenszszerverrel szemben",
"yes": "&#10003;",
"success": "Siker"
"success": "Siker",
"login_page": "Belépő oldal",
"needs_restart": "újraindítást igényel"
},
"edit": {
"active": "Aktív",
@@ -1070,7 +1072,7 @@
"post_domain_add": "A \"sogo-mailcow\" SOGo konténert újra kell indítani egy új tartomány hozzáadása után!<br><br>Kiegészítésképpen a tartományok DNS-konfigurációját is felül kell vizsgálni. A DNS-konfiguráció jóváhagyása után indítsa újra az \"acme-mailcow\"-t, hogy automatikusan generáljon tanúsítványokat az új tartományhoz (autoconfig.&lt;domain&gt;, autodiscover.&lt;domain&gt;).<br>Ez a lépés opcionális, és 24 óránként megismétlődik.",
"dry": "Szinkronizálás szimulálása",
"inactive": "Inaktív",
"kind": "Kedves",
"kind": "Típus",
"mailbox_quota_m": "Maximális kvóta postafiókonként (MiB)",
"mailbox_username": "Felhasználónév (az e-mail cím bal oldali része)",
"max_aliases": "Max. lehetséges álnevek",
@@ -1092,9 +1094,9 @@
"exclude": "Objektumok kizárása (regex)",
"full_name": "Teljes név",
"gal": "Globális címlista",
"goto_ham": "Tanulj <span class=\"text-success\"><b>sonkaként</b></span>",
"goto_ham": "Tanítás <span class=\"text-success\"><b>valódi</b></span> levélként",
"goto_null": "Leveleket csendben eldobni",
"goto_spam": "Tanuld <span class=\"text-danger\"><b>spamként</b></span>",
"goto_spam": "Tanítás <span class=\"text-danger\"><b>spam</b></span>ként",
"syncjob_hint": "Ne feledje, hogy a jelszavakat egyszerű szöveges formában kell elmenteni!",
"target_address": "Továbbítási címek",
"target_address_info": "<small>Teljes e-mail cím(ek) (vesszővel elválasztva).</small>",
@@ -1102,7 +1104,7 @@
"comment_info": "A privát megjegyzés nem látható a felhasználó számára, míg a nyilvános megjegyzés tooltip-ként jelenik meg, amikor a felhasználó áttekintésében a megjegyzésre mutat.",
"custom_params": "Egyéni paraméterek",
"gal_info": "A GAL tartalmazza a tartomány összes objektumát, és egyetlen felhasználó sem szerkesztheti. A SOGo-ban a Szabad/Elfoglalt információ hiányzik, ha ki van kapcsolva! <b>Indítsa újra a SOGo-t a változások alkalmazásához.</b>",
"hostname": "Házigazda",
"hostname": "Hoszt",
"backup_mx_options": "Továbbítási opciók",
"custom_params_hint": "Megfelelő: --param=xy, Rossz: --param xy",
"delete1": "Törlés a forrásból, ha befejeződött",
@@ -1140,6 +1142,109 @@
"sieve_type": "Szűrő típusa",
"skipcrossduplicates": "Duplikált üzenetek átugrása mappák között (érkezési sorrendben)",
"subscribeall": "Feliratkozás minden mappára",
"syncjob": "Szinkronizálási feladat hozzáadása"
"syncjob": "Szinkronizálási feladat hozzáadása",
"internal": "Belső",
"internal_info": "Belső álnevek csak a saját domain vagy domain álnév számára elérhető."
},
"danger": {
"access_denied": "Hozzáférés megtagatva vagy nem megfelelő űrlap adat",
"alias_domain_invalid": "Az alias domain %s érvénytelen",
"alias_empty": "Az alias cím nem lehet üres",
"alias_goto_identical": "Az alias és a goto cím nem lehetnek azonosak",
"alias_invalid": "Az alias cím %s érvénytelen",
"aliasd_targetd_identical": "Az alias tartomány nem lehet azonos a céltartománnyal: %s",
"aliases_in_use": "A maximális aliasoknak nagyobbnak vagy egyenlőnek kell lenniük mint %d",
"app_name_empty": "Az alkalmazás neve nem lehet üres",
"app_passwd_id_invalid": "Alkalmazás jelszó ID %s érvénytelen",
"authsource_in_use": "A személyazonosság szolgáltatót nem lehet megváltoztatni vagy törölni, mivel ez jelenleg használatban van legalább 1 felhasználónál.",
"bcc_empty": "BCC cél nem lehet üres",
"bcc_exists": "A %s típushoz létezik egy %s típusú BCC térkép.",
"bcc_must_be_email": "A BCC cél %s nem érvényes e-mail cím",
"comment_too_long": "Túl hosszú megjegyzés, max 160 karakter megengedett",
"cors_invalid_method": "Érvénytelen Allow-Method lett megadva",
"cors_invalid_origin": "Érvénytelen Allow-Origin lett megadva",
"defquota_empty": "A postafiókonkénti alapértelmezett kvóta nem lehet 0.",
"demo_mode_enabled": "Demo mód engedélyezve",
"description_invalid": "A %s erőforrás leírása érvénytelen",
"dkim_domain_or_sel_exists": "A \"%s\" DKIM-kulcs létezik, és nem lesz felülírva",
"dkim_domain_or_sel_invalid": "DKIM tartomány vagy szelektor érvénytelen: %s",
"domain_cannot_match_hostname": "A tartomány nem egyezik a hostnévvel",
"domain_exists": "A %s domain már létezik",
"domain_invalid": "A domain név üres vagy érvénytelen",
"domain_not_empty": "Nem lehet eltávolítani a nem üres domaint %s",
"domain_not_found": "Nem található domain %s",
"domain_quota_m_in_use": "A domain kvótának nagyobbnak vagy egyenlőnek kell lennie %s MiB-nál",
"extended_sender_acl_denied": "hiányzó ACL külső küldő cím beállításához",
"extra_acl_invalid": "A \"%s\" külső feladó címe érvénytelen",
"extra_acl_invalid_domain": "Külső feladó \"%s\" érvénytelen tartományt használ",
"fido2_verification_failed": "FIDO2 ellenőrzés sikertelen: %s",
"file_open_error": "A fájl nem nyitható meg írásra",
"filter_type": "Rossz szűrőtípus",
"from_invalid": "A feladó nem lehet üres",
"generic_server_error": "Váratlan szerver hiba keletkezett. Vedd fel a kapcsolatot az adminisztrátorral.",
"global_filter_write_error": "Nem tudott szűrőfájlt írni: %s",
"global_map_invalid": "Globális térkép azonosítója %s érvénytelen",
"global_map_write_error": "Nem tudott globális térképet írni ID %s: %s",
"goto_empty": "Egy alias címnek legalább egy érvényes goto címet kell tartalmaznia.",
"goto_invalid": "Goto cím %s érvénytelen",
"ham_learn_error": "Ham tanulási hiba: %s",
"iam_test_connection": "Kapcsolódás sikertelen",
"imagick_exception": "Hiba: Kép olvasása közben Imagick hiba keletkezett",
"img_dimensions_exceeded": "A kép meghaladja a maximális méretet",
"img_invalid": "A képfájlt nem lehet érvényesíteni",
"img_size_exceeded": "A kép meghaladja a maximális fájl méretet",
"img_tmp_missing": "A képfájlt nem lehet érvényesíteni: Ideiglenes fájl nem található",
"invalid_bcc_map_type": "Érvénytelen a BCC térkép típusa",
"invalid_destination": "A \"%s\" célállomás formátum érvénytelen",
"invalid_filter_type": "Érvénytelen szűrőtípus",
"invalid_host": "Érvénytelen host megadva: %s",
"invalid_mime_type": "Érvénytelen mime típus",
"invalid_nexthop": "A következő ugrás formátuma érvénytelen",
"invalid_nexthop_authenticated": "A következő ugrás más hitelesítő adatokkal létezik, kérjük, először frissítse a meglévő hitelesítő adatokat ehhez a következő ugráshoz.",
"invalid_recipient_map_new": "Érvénytelen új címzett megadása: %s",
"invalid_recipient_map_old": "Érvénytelen eredeti címzett van megadva: %s",
"invalid_reset_token": "Érvénytelen visszaállító kulcs",
"ip_list_empty": "Az engedélyezett IP-k listája nem lehet üres",
"is_alias": "%s már ismert álnév címként",
"is_alias_or_mailbox": "%s már ismert alias, egy postafiók vagy egy alias tartományból kiterjesztett alias cím.",
"is_spam_alias": "%s már ismert ideiglenes alias cím (spam alias cím)",
"last_key": "Az utolsó kulcs nem törölhető, kérjük, helyette deaktiválja a TFA-t.",
"login_failed": "A bejelentkezés sikertelen",
"mailbox_defquota_exceeds_mailbox_maxquota": "Az alapértelmezett kvóta meghaladja a maximális kvótakorlátot",
"mailbox_invalid": "A postafiók neve érvénytelen",
"mailbox_quota_exceeded": "A kvóta meghaladja a tartományi korlátot (max. %d MiB)",
"mailbox_quota_exceeds_domain_quota": "A maximális kvóta meghaladja a tartományi kvótakorlátot",
"mailbox_quota_left_exceeded": "Nincs elég hely (maradék hely: %d MiB)",
"mailboxes_in_use": "A maximális postafiókoknak nagyobbnak vagy egyenlőnek kell lenniük %d-vel.",
"malformed_username": "Hibás felhasználónév",
"map_content_empty": "A térkép tartalma nem lehet üres",
"max_age_invalid": "Maximális kor %s érvénytelen",
"max_alias_exceeded": "Max. aliasok túllépése",
"max_mailbox_exceeded": "Max. postafiókok túllépése (%d %d-ből %d)",
"max_quota_in_use": "A postafiók kvótának nagyobbnak vagy egyenlőnek kell lennie %d MiB-nél",
"maxquota_empty": "A postafiókonkénti maximális kvóta nem lehet 0.",
"mode_invalid": "%s mód érvénytelen",
"mx_invalid": "%s MX rekord érvénytelen",
"mysql_error": "MySQL hiba: %s",
"network_host_invalid": "Érvénytelen hálózat vagy állomás: %s",
"next_hop_interferes": "%s zavarja a nexthop %s-t",
"next_hop_interferes_any": "Egy meglévő következő ugrás zavarja a %s-t.",
"nginx_reload_failed": "Az Nginx újratöltése sikertelen: %s",
"no_user_defined": "Nincs felhasználó által meghatározott",
"object_exists": "Az objektum %s már létezik",
"object_is_not_numeric": "Az érték %s nem numerikus",
"password_complexity": "A jelszó nem felel meg a szabályzatnak",
"password_empty": "A jelszó nem lehet üres",
"password_mismatch": "A megerősítő jelszó nem egyezik",
"password_reset_invalid_user": "A fiók nem található vagy nem lett megadva visszaállításhoz email cím",
"password_reset_na": "A jelszó visszaállítás jelenleg nem elérhető. Vedd fel a kapcsolatot az adminisztrátorral.",
"policy_list_from_exists": "A megadott nevű rekord létezik",
"policy_list_from_invalid": "A rekord érvénytelen formátumú",
"private_key_error": "Privát kulcs hiba: %s",
"pushover_credentials_missing": "Pushover token és/vagy kulcs hiányzik",
"pushover_key": "A pushover kulcs rossz formátumú",
"pushover_token": "A Pushover token rossz formátumú",
"quota_not_0_not_numeric": "A kvótának numerikusnak és >= 0-nak kell lennie.",
"recipient_map_entry_exists": "Létezik egy \"%s\" címzett-térkép bejegyzés"
}
}

View File

@@ -675,7 +675,7 @@
"timeout1": "Limit czasu połączenia z serwerem zdalnym",
"timeout2": "Limit czasu połączenia z serwerem lokalnym",
"validate_save": "Zatwierdź i zapisz",
"pushover_info": "Ustawienia powiadomień push będą miały zastosowanie do wszystkich czystych (niespamowych) wiadomości dostarczanych do <b>%s</b>, w tym aliasów (współdzielonych, niewspółdzielonych, oznaczonych)",
"pushover_info": "Ustawienia powiadomień push będą miały zastosowanie do wszystkich czystych (niespamowych) wiadomości dostarczanych do <b>%s</b> w tym aliasów (współdzielonych, niewspółdzielonych, oznaczonych).",
"mailbox_quota_def": "Domyślny limit skrzynki pocztowej",
"mailbox_relayhost_info": "Dotyczy wyłącznie skrzynki pocztowej i bezpośrednich aliasów, nadpisuje ustawienie serwera pośredniczącego (relayhost) dla domeny.",
"maxbytespersecond": "Max. Ilość bajtów na sekundę <br><small>(0 = unlimited)</small>",
@@ -687,7 +687,26 @@
"mbox_rl_info": "Ten limit szybkości dotyczy nazwy logowania SASL i odpowiada dowolnemu adresowi „from” używanemu przez zalogowanego użytkownika. Limit szybkości dla skrzynki pocztowej nadpisuje limit szybkości dla całej domeny.",
"nexthop": "Następny hop",
"private_comment": "Prywatny komentarz",
"public_comment": "Komentarz publiczny"
"public_comment": "Komentarz publiczny",
"mta_sts": "Konfiguruj MTA-STS",
"mta_sts_info": "<a\n\nhref='https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security' target='_blank'>MTA-STS</a> to standard wymuszający dostarczanie poczty elektronicznej pomiędzy serwerami pocztowymi z użyciem TLS oraz ważnych certyfikatów.\n\nJest stosowany wtedy, gdy użycie DANE nie jest możliwe z powodu braku lub nieobsługiwanego DNSSEC.\n\n<br><b>Uwaga</b>: Jeżeli domena odbiorcza obsługuje DANE z DNSSEC, DANE jest <b>zawsze</b> preferowane — MTA-STS działa wyłącznie jako mechanizm zapasowy.",
"mta_sts_version": "Wersja.",
"mta_sts_version_info": "Określa wersję standardu MTA-STS — obecnie jedyną prawidłową wartością jest <code>STSv1</code>..",
"mta_sts_mode": "Tryb.",
"mta_sts_mode_info": "Dostępne są trzy tryby do wyboru:\n<ul> <li><em>testing</em> polityka jest wyłącznie monitorowana, a naruszenia nie mają wpływu na dostarczanie poczty.</li> <li><em>enforce</em> polityka jest ściśle egzekwowana; połączenia bez ważnego TLS są odrzucane.</li> <li><em>none</em> polityka jest publikowana, lecz nie jest stosowana.</li> </ul>.",
"mta_sts_max_age": "Maksymalny czas obowiązywania.",
"mta_sts_max_age_info": "Czas (w sekundach) przechowywania polityki w cache przez serwery odbierające..",
"mta_sts_mx": "serwer MX.",
"mta_sts_mx_info": "Umożliwia wysyłanie poczty wyłącznie do jawnie wymienionych nazw hostów serwerów pocztowych; wysyłający MTA sprawdza, czy nazwa hosta MX w DNS odpowiada liście z polityki, i zezwala na dostarczenie tylko przy użyciu ważnego certyfikatu TLS (ochrona przed atakami MITM)..",
"mta_sts_mx_notice": "Dopuszcza się podanie wielu serwerów MX, rozdzielonych przecinkami..",
"none_inherit": "Brak /Dziedzicz",
"password_recovery_email": "Email do odzyskiwania hasła",
"pushover": "Pushover",
"pushover_evaluate_x_prio": "Eskaluj wiadomości o wysokim priorytecie [<code>X-Priority: 1</code>]",
"pushover_only_x_prio": "Uwzględniaj wyłącznie wiadomości o wysokim priorytecie [<code>X-Priority: 1</code>]",
"pushover_sender_array": "Uwzględniaj wyłącznie następujące adresy e-mail nadawców <small>(oddzielone przecinkami)</small>",
"pushover_sender_regex": "Bierz pod uwagę następujący regex nadawcy",
"pushover_text": "Tekst powiadomienia"
},
"footer": {
"cancel": "Anuluj",
@@ -844,7 +863,14 @@
"template": "Szablon",
"tls_map_dest": "Miejsce docelowe",
"tls_map_dest_info": "Przykłady: example.org, .example.org, [mail.example.org]:25",
"tls_map_parameters": "Parametry"
"tls_map_parameters": "Parametry",
"add_recipient_map_entry": "Dodaj mapę odbiorców",
"add_template": "Dodaj szablon",
"add_tls_policy_map": "Dodaj mapę polityk TLS",
"address_rewriting": "Przepisywanie adresów",
"alias_domain_alias_hint": "Aliasy <b>nie</b> są automatycznie stosowane do aliasów domen. Adres aliasu <code>my-alias@domain</code> <b>nie</b> obejmuje adresu <code>my-alias@alias-domain</code> (gdzie „alias-domain” jest przykładową domeną aliasową dla „domain”).\n<br> Aby przekierować pocztę do zewnętrznej skrzynki, użyj filtra Sieve (zob. kartę „Filtry” lub SOGo → Przekazywanie). Skorzystaj z opcji „Rozszerz alias na domeny aliasowe”, aby automatycznie dodać brakujące aliasy.",
"alias_domain_backupmx": "Domena aliasowa nieaktywna dla domeny przekaźnikowej",
"all_domains": "Wszystkie domeny"
},
"quarantine": {
"action": "Działanie",
@@ -1179,7 +1205,8 @@
"waiting": "Oczekuje",
"with_app_password": "z hasłem aplikacji",
"year": "rok",
"years": "lata"
"years": "lata",
"spam_aliases_info": "Alias antyspamowy to tymczasowy adres e-mail, który może być używany do ochrony właściwych adresów pocztowych. <br>Opcjonalnie można ustawić czas wygaśnięcia, po którym alias zostanie automatycznie dezaktywowany, co pozwala skutecznie pozbyć się nadużywanych lub ujawnionych adresów."
},
"warning": {
"session_ua": "Nieprawidłowy token formularza: Błąd walidacji User-Agent",

View File

@@ -582,13 +582,13 @@
"username": "用户名",
"container_disabled": "容器已被停止或禁用",
"container_running": "运行中",
"cores": "核心数",
"cores": "核",
"memory": "内存",
"error_show_ip": "无法解析公网IP地址",
"show_ip": "显示公网IP",
"update_available": "有可用更新",
"update_failed": "无法检查更新",
"architecture": "构",
"architecture": "构",
"container_stopped": "已停止",
"current_time": "系统时间",
"timezone": "时区",
@@ -1321,7 +1321,7 @@
"sogo_profile_reset": "重置 SOGo 个人资料",
"sogo_profile_reset_help": "此操作会不可恢复地删除用户的 SOGo 个人资料并<b>删除所有联系人和日历数据</b>。",
"sogo_profile_reset_now": "立即重置个人资料",
"spam_aliases": "临时邮箱别名",
"spam_aliases": "垃圾邮件别名",
"spam_score_reset": "重置为服务器默认值",
"spamfilter": "垃圾邮件过滤器",
"spamfilter_behavior": "分数",
@@ -1381,7 +1381,10 @@
"protocols": "协议",
"authentication": "认证",
"tfa_info": "两步验证有助于保护您的账户安全。启用后,对于不支持两步验证的应用程序或服务(例如邮件客户端),需要使用应用专用密码进行登录。",
"overview": "概览"
"overview": "概览",
"expire_never": "永不过期",
"forever": "永久",
"spam_aliases_info": "垃圾邮件别名是一种临时电子邮件地址,可用于保护真实电子邮件地址。<br>还可以选择设置过期时间,以便在设定的时间后自动停用别名,从而有效地销毁被滥用或泄露的地址。"
},
"warning": {
"cannot_delete_self": "不能删除已登录的用户",

View File

@@ -34,15 +34,15 @@ catch(PDOException $e) {
if (isset($_GET['only_email'])) {
$onlyEmailAccount = true;
$description = 'IMAP';
$description = 'IMAP';
} else {
$onlyEmailAccount = false;
$description = 'IMAP, CalDAV, CardDAV';
$description = 'IMAP, CalDAV, CardDAV';
}
if (isset($_GET['app_password'])) {
$app_password = true;
$description .= ' with application password';
if (strpos($_SERVER['HTTP_USER_AGENT'], 'iPad') !== FALSE)
$platform = 'iPad';
elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') !== FALSE)
@@ -51,8 +51,9 @@ if (isset($_GET['app_password'])) {
$platform = 'Mac';
else
$platform = $_SERVER['HTTP_USER_AGENT'];
$password = bin2hex(openssl_random_pseudo_bytes(16));
$password = password_generate();
$attr = array(
'app_name' => $platform,
'app_passwd' => $password,

View File

@@ -12,18 +12,21 @@ $session_var_pass = 'sogo-sso-pass';
if (isset($_SERVER['PHP_AUTH_USER'])) {
// load prerequisites only when required
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
$username = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
$is_eas = false;
$is_dav = false;
// Determine service type for protocol access check
$service = 'NONE';
$original_uri = isset($_SERVER['HTTP_X_ORIGINAL_URI']) ? $_SERVER['HTTP_X_ORIGINAL_URI'] : '';
if (preg_match('/^(\/SOGo|)\/dav.*/', $original_uri) === 1) {
$is_dav = true;
$service = 'DAV';
}
elseif (preg_match('/^(\/SOGo|)\/Microsoft-Server-ActiveSync.*/', $original_uri) === 1) {
$is_eas = true;
$service = 'EAS';
}
$login_check = check_login($username, $password, array('dav' => $is_dav, 'eas' => $is_eas));
$login_check = check_login($username, $password, array('service' => $service));
if ($login_check === 'user') {
header("X-User: $username");
header("X-Auth: Basic ".base64_encode("$username:$password"));
@@ -57,7 +60,6 @@ elseif (isset($_GET['login'])) {
$_SESSION['mailcow_cc_role'] = "user";
}
// update sasl logs
$service = ($app_passwd_data['eas'] === true) ? 'EAS' : 'DAV';
$stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES ('SSO', 0, :username, :remote_addr)");
$stmt->execute(array(
':username' => $login,

View File

@@ -108,6 +108,8 @@
<option value="pop3"{% if template.attributes.pop3_access == '1' %} selected{% endif %}>POP3</option>
<option value="smtp"{% if template.attributes.smtp_access == '1' %} selected{% endif %}>SMTP</option>
<option value="sieve"{% if template.attributes.sieve_access == '1' %} selected{% endif %}>Sieve</option>
<option value="eas"{% if template.attributes.eas_access == '1' %} selected{% endif %}>ActiveSync</option>
<option value="dav"{% if template.attributes.dav_access == '1' %} selected{% endif %}>CalDAV/CardDAV</option>
</select>
</div>
</div>

View File

@@ -287,6 +287,8 @@
<option value="pop3"{% if result.attributes.pop3_access == '1' %} selected{% endif %}>POP3</option>
<option value="smtp"{% if result.attributes.smtp_access == '1' %} selected{% endif %}>SMTP</option>
<option value="sieve"{% if result.attributes.sieve_access == '1' %} selected{% endif %}>Sieve</option>
<option value="eas"{% if result.attributes.eas_access == '1' %} selected{% endif %}>ActiveSync</option>
<option value="dav"{% if result.attributes.dav_access == '1' %} selected{% endif %}>CalDAV/CardDAV</option>
</select>
</div>
</div>

View File

@@ -148,6 +148,8 @@
<option value="pop3">POP3</option>
<option value="smtp">SMTP</option>
<option value="sieve">Sieve</option>
<option value="eas">ActiveSync</option>
<option value="dav">CalDAV/CardDAV</option>
</select>
</div>
</div>
@@ -335,6 +337,8 @@
<option value="pop3" selected>POP3</option>
<option value="smtp" selected>SMTP</option>
<option value="sieve" selected>Sieve</option>
<option value="activesync" selected>ActiveSync</option>
<option value="dav" selected>CalDAV/CardDAV</option>
</select>
</div>
</div>

View File

@@ -55,6 +55,8 @@
{% if mailboxdata.attributes.smtp_access == 1 %}<div class="badge fs-6 bg-success m-2">SMTP <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">SMTP <i class="bi bi-x-lg"></i></div>{% endif %}
{% if mailboxdata.attributes.sieve_access == 1 %}<div class="badge fs-6 bg-success m-2">Sieve <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">Sieve <i class="bi bi-x-lg"></i></div>{% endif %}
{% if mailboxdata.attributes.pop3_access == 1 %}<div class="badge fs-6 bg-success m-2">POP3 <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">POP3 <i class="bi bi-x-lg"></i></div>{% endif %}
{% if mailboxdata.attributes.eas_access == 1 %}<div class="badge fs-6 bg-success m-2">ActiveSync <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">ActiveSync <i class="bi bi-x-lg"></i></div>{% endif %}
{% if mailboxdata.attributes.dav_access == 1 %}<div class="badge fs-6 bg-success m-2">CalDAV/CardDAV <i class="bi bi-check-lg"></i></div>{% else %}<div class="badge fs-6 bg-danger m-2">CalDAV/CardDAV <i class="bi bi-x-lg"></i></div>{% endif %}
</div>
</div>
</div>