diff --git a/data/Dockerfiles/dovecot/Dockerfile b/data/Dockerfiles/dovecot/Dockerfile index 10e141ab8..f1152c8a1 100644 --- a/data/Dockerfiles/dovecot/Dockerfile +++ b/data/Dockerfiles/dovecot/Dockerfile @@ -3,7 +3,7 @@ FROM alpine:3.21 LABEL maintainer="The Infrastructure Company GmbH " # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?.*)$ -ARG GOSU_VERSION=1.17 +ARG GOSU_VERSION=1.19 ENV LANG=C.UTF-8 ENV LC_ALL=C.UTF-8 diff --git a/data/Dockerfiles/phpfpm/Dockerfile b/data/Dockerfiles/phpfpm/Dockerfile index b8a7a432a..f0971ff5e 100644 --- a/data/Dockerfiles/phpfpm/Dockerfile +++ b/data/Dockerfiles/phpfpm/Dockerfile @@ -3,15 +3,15 @@ FROM php:8.2-fpm-alpine3.21 LABEL maintainer = "The Infrastructure Company GmbH " # renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?.*)$ -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=(?.*)$ -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(?.*)$ ARG MAILPARSE_PECL_VERSION=3.1.9 # renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?.*)$ -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=(?.*)$ -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=(?.*)$ ARG COMPOSER_VERSION=2.8.6 diff --git a/data/Dockerfiles/sogo/Dockerfile b/data/Dockerfiles/sogo/Dockerfile index f2981ad04..ed7e07ed6 100644 --- a/data/Dockerfiles/sogo/Dockerfile +++ b/data/Dockerfiles/sogo/Dockerfile @@ -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=^(?.*)$ -ARG GOSU_VERSION=1.17 +ARG GOSU_VERSION=1.19 ENV LC_ALL=C # Prerequisites diff --git a/data/conf/dovecot/auth/mailcowauth.php b/data/conf/dovecot/auth/mailcowauth.php index 06c0bd995..d1c7381f6 100644 --- a/data/conf/dovecot/auth/mailcowauth.php +++ b/data/conf/dovecot/auth/mailcowauth.php @@ -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){ diff --git a/data/conf/postfix/postscreen_access.cidr b/data/conf/postfix/postscreen_access.cidr index 694b98636..9ff9f6265 100644 --- a/data/conf/postfix/postscreen_access.cidr +++ b/data/conf/postfix/postscreen_access.cidr @@ -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 diff --git a/data/web/autoconfig.php b/data/web/autoconfig.php index 6a528d4a2..bb4a55cb6 100644 --- a/data/web/autoconfig.php +++ b/data/web/autoconfig.php @@ -29,8 +29,8 @@ header('Content-Type: application/xml'); %EMAILDOMAIN% - A mailcow mail server - mail server + + diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php index 224f94f71..d3cda4004 100644 --- a/data/web/autodiscover.php +++ b/data/web/autodiscover.php @@ -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"); diff --git a/data/web/inc/functions.auth.inc.php b/data/web/inc/functions.auth.inc.php index 059dd4cd9..3903ba642 100644 --- a/data/web/inc/functions.auth.inc.php +++ b/data/web/inc/functions.auth.inc.php @@ -1,10 +1,11 @@ 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; } diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 1947ec465..23b8d701d 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -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'); diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index a4147ee91..8d2efea3d 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -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){ diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index 37589bac3..c64419800 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -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;"); diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index deb5da8fa..198e675aa 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -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; diff --git a/data/web/inc/triggers.admin.inc.php b/data/web/inc/triggers.admin.inc.php index df46a459c..2a02ba511 100644 --- a/data/web/inc/triggers.admin.inc.php +++ b/data/web/inc/triggers.admin.inc.php @@ -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); diff --git a/data/web/inc/triggers.domainadmin.inc.php b/data/web/inc/triggers.domainadmin.inc.php index a9f913688..764d9009b 100644 --- a/data/web/inc/triggers.domainadmin.inc.php +++ b/data/web/inc/triggers.domainadmin.inc.php @@ -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); diff --git a/data/web/inc/triggers.user.inc.php b/data/web/inc/triggers.user.inc.php index 36176c694..cc33596f9 100644 --- a/data/web/inc/triggers.user.inc.php +++ b/data/web/inc/triggers.user.inc.php @@ -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); diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index 9f3208e3d..6d1965542 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -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 diff --git a/data/web/index.php b/data/web/index.php index d4fa46e74..a1ff9310f 100644 --- a/data/web/index.php +++ b/data/web/index.php @@ -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']; diff --git a/data/web/js/site/mailbox.js b/data/web/js/site/mailbox.js index df61f8720..7010077db 100644 --- a/data/web/js/site/mailbox.js +++ b/data/web/js/site/mailbox.js @@ -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 = ''; item.smtp_access = ''; item.sieve_access = ''; + item.eas_access = ''; + item.dav_access = ''; 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 = '' + (item.attributes.imap_access == 1 ? '1' : '0') + ''; item.attributes.smtp_access = '' + (item.attributes.smtp_access == 1 ? '1' : '0') + ''; item.attributes.sieve_access = '' + (item.attributes.sieve_access == 1 ? '1' : '0') + ''; + item.attributes.eas_access = '' + (item.attributes.eas_access == 1 ? '1' : '0') + ''; + item.attributes.dav_access = '' + (item.attributes.dav_access == 1 ? '1' : '0') + ''; item.attributes.sogo_access = '' + (item.attributes.sogo_access == 1 ? '1' : '0') + ''; 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', diff --git a/data/web/lang/lang.hu-hu.json b/data/web/lang/lang.hu-hu.json index 2f78d5b21..e51748c48 100644 --- a/data/web/lang/lang.hu-hu.json +++ b/data/web/lang/lang.hu-hu.json @@ -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": "✓", - "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!

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.<domain>, autodiscover.<domain>).
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 sonkaként", + "goto_ham": "Tanítás valódi levélként", "goto_null": "Leveleket csendben eldobni", - "goto_spam": "Tanuld spamként", + "goto_spam": "Tanítás spamké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": "Teljes e-mail cím(ek) (vesszővel elválasztva).", @@ -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! Indítsa újra a SOGo-t a változások alkalmazásához.", - "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" } } diff --git a/data/web/lang/lang.pl-pl.json b/data/web/lang/lang.pl-pl.json index d78d46f7a..758d0cb40 100644 --- a/data/web/lang/lang.pl-pl.json +++ b/data/web/lang/lang.pl-pl.json @@ -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 %s, 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 %s 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ę
(0 = unlimited)", @@ -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": "MTA-STS 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
Uwaga: Jeżeli domena odbiorcza obsługuje DANE z DNSSEC, DANE jest zawsze 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 STSv1..", + "mta_sts_mode": "Tryb.", + "mta_sts_mode_info": "Dostępne są trzy tryby do wyboru:\n
  • testing – polityka jest wyłącznie monitorowana, a naruszenia nie mają wpływu na dostarczanie poczty.
  • enforce – polityka jest ściśle egzekwowana; połączenia bez ważnego TLS są odrzucane.
  • none – polityka jest publikowana, lecz nie jest stosowana.
.", + "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 [X-Priority: 1]", + "pushover_only_x_prio": "Uwzględniaj wyłącznie wiadomości o wysokim priorytecie [X-Priority: 1]", + "pushover_sender_array": "Uwzględniaj wyłącznie następujące adresy e-mail nadawców (oddzielone przecinkami)", + "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 nie są automatycznie stosowane do aliasów domen. Adres aliasu my-alias@domain nie obejmuje adresu my-alias@alias-domain (gdzie „alias-domain” jest przykładową domeną aliasową dla „domain”).\n
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.
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", diff --git a/data/web/lang/lang.zh-cn.json b/data/web/lang/lang.zh-cn.json index 94473a405..fe225a5df 100644 --- a/data/web/lang/lang.zh-cn.json +++ b/data/web/lang/lang.zh-cn.json @@ -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 个人资料并删除所有联系人和日历数据。", "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": "垃圾邮件别名是一种临时电子邮件地址,可用于保护真实电子邮件地址。
还可以选择设置过期时间,以便在设定的时间后自动停用别名,从而有效地销毁被滥用或泄露的地址。" }, "warning": { "cannot_delete_self": "不能删除已登录的用户", diff --git a/data/web/mobileconfig.php b/data/web/mobileconfig.php index 44aaa30ae..7c0ead7f5 100644 --- a/data/web/mobileconfig.php +++ b/data/web/mobileconfig.php @@ -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, diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index 962627baf..2da28d4d4 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -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, diff --git a/data/web/templates/edit/mailbox-templates.twig b/data/web/templates/edit/mailbox-templates.twig index 65a83cd2a..ddc139586 100644 --- a/data/web/templates/edit/mailbox-templates.twig +++ b/data/web/templates/edit/mailbox-templates.twig @@ -108,6 +108,8 @@ + + diff --git a/data/web/templates/edit/mailbox.twig b/data/web/templates/edit/mailbox.twig index 0b83dc5bf..abf0c45ba 100644 --- a/data/web/templates/edit/mailbox.twig +++ b/data/web/templates/edit/mailbox.twig @@ -287,6 +287,8 @@ + + diff --git a/data/web/templates/modals/mailbox.twig b/data/web/templates/modals/mailbox.twig index 2e0f589d0..9e1889500 100644 --- a/data/web/templates/modals/mailbox.twig +++ b/data/web/templates/modals/mailbox.twig @@ -148,6 +148,8 @@ + + @@ -335,6 +337,8 @@ + + diff --git a/data/web/templates/user/tab-user-auth.twig b/data/web/templates/user/tab-user-auth.twig index bace33489..171545d6c 100644 --- a/data/web/templates/user/tab-user-auth.twig +++ b/data/web/templates/user/tab-user-auth.twig @@ -55,6 +55,8 @@ {% if mailboxdata.attributes.smtp_access == 1 %}
SMTP
{% else %}
SMTP
{% endif %} {% if mailboxdata.attributes.sieve_access == 1 %}
Sieve
{% else %}
Sieve
{% endif %} {% if mailboxdata.attributes.pop3_access == 1 %}
POP3
{% else %}
POP3
{% endif %} + {% if mailboxdata.attributes.eas_access == 1 %}
ActiveSync
{% else %}
ActiveSync
{% endif %} + {% if mailboxdata.attributes.dav_access == 1 %}
CalDAV/CardDAV
{% else %}
CalDAV/CardDAV
{% endif %}