diff --git a/data/web/admin.php b/data/web/admin.php index cd10f6d4..b1de85a7 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -76,8 +76,8 @@ $tfa_data = get_tfa(); - - API (experimental, work in progress) + + API (experimental, work in progress) + Quota notifications Rspamd settings map @@ -415,8 +416,8 @@ $tfa_data = get_tfa(); - - + +
@@ -436,8 +437,8 @@ $tfa_data = get_tfa();
- - + +
@@ -579,32 +580,36 @@ $tfa_data = get_tfa(); -

() - - - [] - [whitelist] - [blacklist] +

() - + + [] + [whitelist] + [blacklist] + + + +

- +

+ +

-

- -

- -

-
@@ -645,9 +650,12 @@ $tfa_data = get_tfa();
- - -
+ + + +
+ +
@@ -680,13 +688,63 @@ $tfa_data = get_tfa();
+ +
+
Quota notifications
+
+

Quota notications are sent to users once when crossing 80% and once when crossing 95% usage.

+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + + +
+ +
+
+
+
+
+
+
+ +
+
+
+ +
+
+
Rspamd settings map
- Active settings map - -
+ + Active settings map + +
+ +
@@ -836,29 +894,33 @@ $tfa_data = get_tfa();

- + + + +
-
-
- - -
-
- - -
-
- - -
-
- - -
- -
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
diff --git a/data/web/fonts/PTS55F_W.woff b/data/web/fonts/PTS55F_W.woff deleted file mode 100644 index 23ce0902..00000000 Binary files a/data/web/fonts/PTS55F_W.woff and /dev/null differ diff --git a/data/web/fonts/PTS56F_W.woff b/data/web/fonts/PTS56F_W.woff deleted file mode 100644 index bfd7b37d..00000000 Binary files a/data/web/fonts/PTS56F_W.woff and /dev/null differ diff --git a/data/web/fonts/PTS75F_W.woff b/data/web/fonts/PTS75F_W.woff deleted file mode 100644 index 7d5df4e4..00000000 Binary files a/data/web/fonts/PTS75F_W.woff and /dev/null differ diff --git a/data/web/fonts/pt-sans-v9-cyrillic_latin-700.woff b/data/web/fonts/pt-sans-v9-cyrillic_latin-700.woff new file mode 100644 index 00000000..7d6f0c4a Binary files /dev/null and b/data/web/fonts/pt-sans-v9-cyrillic_latin-700.woff differ diff --git a/data/web/fonts/pt-sans-v9-cyrillic_latin-700.woff2 b/data/web/fonts/pt-sans-v9-cyrillic_latin-700.woff2 new file mode 100644 index 00000000..24b3daed Binary files /dev/null and b/data/web/fonts/pt-sans-v9-cyrillic_latin-700.woff2 differ diff --git a/data/web/fonts/pt-sans-v9-cyrillic_latin-italic.woff b/data/web/fonts/pt-sans-v9-cyrillic_latin-italic.woff new file mode 100644 index 00000000..b73c2004 Binary files /dev/null and b/data/web/fonts/pt-sans-v9-cyrillic_latin-italic.woff differ diff --git a/data/web/fonts/pt-sans-v9-cyrillic_latin-italic.woff2 b/data/web/fonts/pt-sans-v9-cyrillic_latin-italic.woff2 new file mode 100644 index 00000000..6d47b145 Binary files /dev/null and b/data/web/fonts/pt-sans-v9-cyrillic_latin-italic.woff2 differ diff --git a/data/web/fonts/pt-sans-v9-cyrillic_latin-regular.woff b/data/web/fonts/pt-sans-v9-cyrillic_latin-regular.woff new file mode 100644 index 00000000..b4223a1f Binary files /dev/null and b/data/web/fonts/pt-sans-v9-cyrillic_latin-regular.woff differ diff --git a/data/web/fonts/pt-sans-v9-cyrillic_latin-regular.woff2 b/data/web/fonts/pt-sans-v9-cyrillic_latin-regular.woff2 new file mode 100644 index 00000000..c1209b46 Binary files /dev/null and b/data/web/fonts/pt-sans-v9-cyrillic_latin-regular.woff2 differ diff --git a/data/web/inc/functions.address_rewriting.inc.php b/data/web/inc/functions.address_rewriting.inc.php index bbf36c42..df67d5b7 100644 --- a/data/web/inc/functions.address_rewriting.inc.php +++ b/data/web/inc/functions.address_rewriting.inc.php @@ -44,8 +44,8 @@ function bcc($_action, $_data = null, $attr = null) { ); return false; } - $domain = idn_to_ascii($local_dest); - $local_dest_sane = '@' . idn_to_ascii($local_dest); + $domain = idn_to_ascii($local_dest, 0, INTL_IDNA_VARIANT_UTS46); + $local_dest_sane = '@' . idn_to_ascii($local_dest, 0, INTL_IDNA_VARIANT_UTS46); } elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) { if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $local_dest)) { @@ -265,7 +265,7 @@ function recipient_map($_action, $_data = null, $attr = null) { $new_dest = strtolower(trim($_data['recipient_map_new'])); $active = intval($_data['active']); if (is_valid_domain_name($old_dest)) { - $old_dest_sane = '@' . idn_to_ascii($old_dest); + $old_dest_sane = '@' . idn_to_ascii($old_dest, 0, INTL_IDNA_VARIANT_UTS46); } elseif (filter_var($old_dest, FILTER_VALIDATE_EMAIL)) { $old_dest_sane = $old_dest; @@ -331,7 +331,7 @@ function recipient_map($_action, $_data = null, $attr = null) { continue; } if (is_valid_domain_name($old_dest)) { - $old_dest_sane = '@' . idn_to_ascii($old_dest); + $old_dest_sane = '@' . idn_to_ascii($old_dest, 0, INTL_IDNA_VARIANT_UTS46); } elseif (filter_var($old_dest, FILTER_VALIDATE_EMAIL)) { $old_dest_sane = $old_dest; diff --git a/data/web/inc/functions.docker.inc.php b/data/web/inc/functions.docker.inc.php index 211a87fb..581919dd 100644 --- a/data/web/inc/functions.docker.inc.php +++ b/data/web/inc/functions.docker.inc.php @@ -32,7 +32,8 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex $containers = json_decode($response, true); if (!empty($containers)) { foreach ($containers as $container) { - if ($container['Config']['Labels']['com.docker.compose.service'] == $service_name + if (isset($container['Config']['Labels']['com.docker.compose.service']) + && $container['Config']['Labels']['com.docker.compose.service'] == $service_name && $container['Config']['Labels']['com.docker.compose.project'] == strtolower(getenv('COMPOSE_PROJECT_NAME'))) { return trim($container['Id']); } @@ -120,7 +121,8 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex if (!empty($decoded_response)) { if (empty($service_name)) { foreach ($decoded_response as $container) { - if ($container['Config']['Labels']['com.docker.compose.project'] == strtolower(getenv('COMPOSE_PROJECT_NAME'))) { + if (isset($container['Config']['Labels']['com.docker.compose.project']) + && $container['Config']['Labels']['com.docker.compose.project'] == strtolower(getenv('COMPOSE_PROJECT_NAME'))) { unset($container['Config']['Env']); $out[$container['Config']['Labels']['com.docker.compose.service']]['State'] = $container['State']; $out[$container['Config']['Labels']['com.docker.compose.service']]['Config'] = $container['Config']; @@ -128,7 +130,8 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex } } else { - if ($decoded_response['Config']['Labels']['com.docker.compose.project'] == strtolower(getenv('COMPOSE_PROJECT_NAME'))) { + if (isset($decoded_response['Config']['Labels']['com.docker.compose.project']) + && $decoded_response['Config']['Labels']['com.docker.compose.project'] == strtolower(getenv('COMPOSE_PROJECT_NAME'))) { unset($container['Config']['Env']); $out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['State'] = $decoded_response['State']; $out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['Config'] = $decoded_response['Config']; diff --git a/data/web/inc/functions.fail2ban.inc.php b/data/web/inc/functions.fail2ban.inc.php index beb1f750..41d682b0 100644 --- a/data/web/inc/functions.fail2ban.inc.php +++ b/data/web/inc/functions.fail2ban.inc.php @@ -118,6 +118,7 @@ function fail2ban($_action, $_data = null) { if (valid_network($network)) { $redis->hSet('F2B_BLACKLIST', $network, 1); $redis->hDel('F2B_WHITELIST', $network, 1); + $response = docker('post', 'netfilter-mailcow', 'restart'); } } } diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index dae26665..277e4c14 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -680,7 +680,7 @@ function is_valid_domain_name($domain_name) { if (empty($domain_name)) { return false; } - $domain_name = idn_to_ascii($domain_name); + $domain_name = idn_to_ascii($domain_name, 0, INTL_IDNA_VARIANT_UTS46); return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name) && preg_match("/^.{1,253}$/", $domain_name) && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)); diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 7166ab6f..3072f683 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -322,7 +322,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); return false; } - $domain = idn_to_ascii(strtolower(trim($_data['domain']))); + $domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46); $description = $_data['description']; $aliases = $_data['aliases']; $mailboxes = $_data['mailboxes']; @@ -493,7 +493,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { if (empty($goto)) { continue; } - $goto_domain = idn_to_ascii(substr(strstr($goto, '@'), 1)); + $goto_domain = idn_to_ascii(substr(strstr($goto, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46); $goto_local_part = strstr($goto, '@', true); $goto = $goto_local_part.'@'.$goto_domain; $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` @@ -531,7 +531,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { if (in_array($address, $gotos)) { continue; } - $domain = idn_to_ascii(substr(strstr($address, '@'), 1)); + $domain = idn_to_ascii(substr(strstr($address, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46); $local_part = strstr($address, '@', true); $address = $local_part.'@'.$domain; $domaindata = mailbox('get', 'domain_details', $domain); @@ -637,7 +637,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $active = intval($_data['active']); $alias_domains = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['alias_domain'])); $alias_domains = array_filter($alias_domains); - $target_domain = idn_to_ascii(strtolower(trim($_data['target_domain']))); + $target_domain = idn_to_ascii(strtolower(trim($_data['target_domain'])), 0, INTL_IDNA_VARIANT_UTS46); if (!isset($_SESSION['acl']['alias_domains']) || $_SESSION['acl']['alias_domains'] != "1" ) { $_SESSION['return'][] = array( 'type' => 'danger', @@ -663,7 +663,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { return false; } foreach ($alias_domains as $i => $alias_domain) { - $alias_domain = idn_to_ascii(strtolower(trim($alias_domain))); + $alias_domain = idn_to_ascii(strtolower(trim($alias_domain)), 0, INTL_IDNA_VARIANT_UTS46); if (!is_valid_domain_name($alias_domain)) { $_SESSION['return'][] = array( 'type' => 'danger', @@ -735,7 +735,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { break; case 'mailbox': $local_part = strtolower(trim($_data['local_part'])); - $domain = idn_to_ascii(strtolower(trim($_data['domain']))); + $domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46); $username = $local_part . '@' . $domain; if (!filter_var($username, FILTER_VALIDATE_EMAIL)) { $_SESSION['return'][] = array( @@ -938,7 +938,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); break; case 'resource': - $domain = idn_to_ascii(strtolower(trim($_data['domain']))); + $domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46); $description = $_data['description']; $local_part = preg_replace('/[^\da-z]/i', '', preg_quote($description, '/')); $name = $local_part . '@' . $domain; @@ -1056,11 +1056,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { case 'alias_domain': $alias_domains = (array)$_data['alias_domain']; foreach ($alias_domains as $alias_domain) { - $alias_domain = idn_to_ascii(strtolower(trim($alias_domain))); + $alias_domain = idn_to_ascii(strtolower(trim($alias_domain)), 0, INTL_IDNA_VARIANT_UTS46); $is_now = mailbox('get', 'alias_domain_details', $alias_domain); if (!empty($is_now)) { $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int']; - $target_domain = (!empty($_data['target_domain'])) ? idn_to_ascii(strtolower(trim($_data['target_domain']))) : $is_now['target_domain']; + $target_domain = (!empty($_data['target_domain'])) ? idn_to_ascii(strtolower(trim($_data['target_domain'])), 0, INTL_IDNA_VARIANT_UTS46) : $is_now['target_domain']; } else { $_SESSION['return'][] = array( @@ -1676,7 +1676,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { continue; } if ($is_now['address'] != $address) { - $domain = idn_to_ascii(substr(strstr($address, '@'), 1)); + $domain = idn_to_ascii(substr(strstr($address, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46); $local_part = strstr($address, '@', true); $address = $local_part.'@'.$domain; if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) { @@ -1810,7 +1810,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $domains = $_data['domain']; } foreach ($domains as $domain) { - $domain = idn_to_ascii($domain); + $domain = idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46); if (!is_valid_domain_name($domain)) { $_SESSION['return'][] = array( 'type' => 'danger', @@ -2887,7 +2887,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { break; case 'domain_details': $domaindata = array(); - $_data = idn_to_ascii(strtolower(trim($_data))); + $_data = idn_to_ascii(strtolower(trim($_data)), 0, INTL_IDNA_VARIANT_UTS46); if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { return false; } @@ -3308,7 +3308,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); continue; } - $domain = idn_to_ascii(strtolower(trim($domain))); + $domain = idn_to_ascii(strtolower(trim($domain)), 0, INTL_IDNA_VARIANT_UTS46); $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `domain` = :domain"); $stmt->execute(array(':domain' => $domain)); diff --git a/data/web/inc/functions.policy.inc.php b/data/web/inc/functions.policy.inc.php index 114e266b..ad29bd24 100644 --- a/data/web/inc/functions.policy.inc.php +++ b/data/web/inc/functions.policy.inc.php @@ -26,7 +26,7 @@ function policy($_action, $_scope, $_data = null) { ); return false; } - $object = idn_to_ascii(strtolower(trim($object))); + $object = idn_to_ascii(strtolower(trim($object)), 0, INTL_IDNA_VARIANT_UTS46); } else { $_SESSION['return'][] = array( @@ -183,7 +183,7 @@ function policy($_action, $_scope, $_data = null) { ); continue; } - $object = idn_to_ascii(strtolower(trim($object))); + $object = idn_to_ascii(strtolower(trim($object)), 0, INTL_IDNA_VARIANT_UTS46); } else { $_SESSION['return'][] = array( @@ -277,7 +277,7 @@ function policy($_action, $_scope, $_data = null) { if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { return false; } - $_data = idn_to_ascii(strtolower(trim($_data))); + $_data = idn_to_ascii(strtolower(trim($_data)), 0, INTL_IDNA_VARIANT_UTS46); } // WHITELIST diff --git a/data/web/inc/functions.quarantine.inc.php b/data/web/inc/functions.quarantine.inc.php index 9d56c743..2b7f71ad 100644 --- a/data/web/inc/functions.quarantine.inc.php +++ b/data/web/inc/functions.quarantine.inc.php @@ -83,7 +83,7 @@ function quarantine($_action, $_data = null) { $max_size = $_data['max_size']; $subject = $_data['subject']; $sender = $_data['sender']; - $html = $_data['html']; + $html = $_data['html_tmpl']; $exclude_domains = (array)$_data['exclude_domains']; try { $redis->Set('Q_RETENTION_SIZE', intval($retention_size)); @@ -436,9 +436,9 @@ function quarantine($_action, $_data = null) { $settings['release_format'] = $redis->Get('Q_RELEASE_FORMAT'); $settings['subject'] = $redis->Get('Q_SUBJ'); $settings['sender'] = $redis->Get('Q_SENDER'); - $settings['html'] = htmlspecialchars($redis->Get('Q_HTML')); - if (empty($settings['html'])) { - $settings['html'] = htmlspecialchars(file_get_contents("/tpls/quarantine.tpl")); + $settings['html_tmpl'] = htmlspecialchars($redis->Get('Q_HTML')); + if (empty($settings['html_tmpl'])) { + $settings['html_tmpl'] = htmlspecialchars(file_get_contents("/tpls/quarantine.tpl")); } } catch (RedisException $e) { diff --git a/data/web/inc/functions.quota_notification.inc.php b/data/web/inc/functions.quota_notification.inc.php new file mode 100644 index 00000000..61d101dc --- /dev/null +++ b/data/web/inc/functions.quota_notification.inc.php @@ -0,0 +1,65 @@ + 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'access_denied' + ); + return false; + } + switch ($_action) { + case 'edit': + $retention_size = $_data['retention_size']; + if ($_data['release_format'] == 'attachment' || $_data['release_format'] == 'raw') { + $release_format = $_data['release_format']; + } + else { + $release_format = 'raw'; + } + $subject = $_data['subject']; + $sender = $_data['sender']; + $html = $_data['html_tmpl']; + try { + $redis->Set('QW_SENDER', $sender); + $redis->Set('QW_SUBJ', $subject); + $redis->Set('QW_HTML', $html); + } + catch (RedisException $e) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('redis_error', $e) + ); + return false; + } + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'saved_settings' + ); + break; + case 'get': + try { + $settings['subject'] = $redis->Get('QW_SUBJ'); + $settings['sender'] = $redis->Get('QW_SENDER'); + $settings['html_tmpl'] = htmlspecialchars($redis->Get('QW_HTML')); + if (empty($settings['html_tmpl'])) { + $settings['html_tmpl'] = htmlspecialchars(file_get_contents("/tpls/quota.tpl")); + } + } + catch (RedisException $e) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('redis_error', $e) + ); + return false; + } + return $settings; + break; + } +} diff --git a/data/web/inc/functions.tls_policy_maps.inc.php b/data/web/inc/functions.tls_policy_maps.inc.php index 6581b60d..70f9f8f0 100644 --- a/data/web/inc/functions.tls_policy_maps.inc.php +++ b/data/web/inc/functions.tls_policy_maps.inc.php @@ -7,7 +7,7 @@ function tls_policy_maps($_action, $_data = null, $attr = null) { } switch ($_action) { case 'add': - $dest = idn_to_ascii(trim($_data['dest'])); + $dest = idn_to_ascii(trim($_data['dest']), 0, INTL_IDNA_VARIANT_UTS46); $policy = strtolower(trim($_data['policy'])); $parameters = (isset($_data['parameters']) && !empty($_data['parameters'])) ? $_data['parameters'] : ''; if (!empty($parameters)) { diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php index fe58e7f4..8f343d0b 100644 --- a/data/web/inc/header.inc.php +++ b/data/web/inc/header.inc.php @@ -7,10 +7,6 @@ <?=$UI_TEXTS['title_name'];?> - $val): - ?> + if ($app_links) { + foreach ($app_links as $row) { + foreach ($row as $key => $val): + ?>
  • - diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index a16ecc48..bd9b908e 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -157,6 +157,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.address_rewriting.inc.p require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.domain_admin.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.admin.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.quarantine.inc.php'; +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.quota_notification.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.dkim.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php'; diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index 1b4a25f3..76ab0fdb 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -44,18 +44,18 @@ $autodiscover_config = array( // The autoconfig service will additionally announce the STARTTLS-enabled ports, specified in the "tlsport" variable. 'imap' => array( 'server' => $mailcow_hostname, - 'port' => array_pop(explode(':', getenv('IMAPS_PORT'))), - 'tlsport' => array_pop(explode(':', getenv('IMAP_PORT'))), + 'port' => array_key_last(explode(':', getenv('IMAPS_PORT'))), + 'tlsport' => array_key_last(explode(':', getenv('IMAP_PORT'))), ), 'pop3' => array( 'server' => $mailcow_hostname, - 'port' => array_pop(explode(':', getenv('POPS_PORT'))), - 'tlsport' => array_pop(explode(':', getenv('POP_PORT'))), + 'port' => array_key_last(explode(':', getenv('POPS_PORT'))), + 'tlsport' => array_key_last(explode(':', getenv('POP_PORT'))), ), 'smtp' => array( 'server' => $mailcow_hostname, - 'port' => array_pop(explode(':', getenv('SMTPS_PORT'))), - 'tlsport' => array_pop(explode(':', getenv('SUBMISSION_PORT'))), + 'port' => array_key_last(explode(':', getenv('SMTPS_PORT'))), + 'tlsport' => array_key_last(explode(':', getenv('SUBMISSION_PORT'))), ), 'activesync' => array( 'url' => 'https://'.$mailcow_hostname.($https_port == 443 ? '' : ':'.$https_port).'/Microsoft-Server-ActiveSync', diff --git a/data/web/json_api.php b/data/web/json_api.php index df27737b..53ecf520 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -1161,6 +1161,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u case "quarantine": process_edit_return(quarantine('edit', $attr)); break; + case "quota_notification": + process_edit_return(quota_notification('edit', $attr)); + break; case "mailq": process_edit_return(mailq('edit', array_merge(array('qid' => $items), $attr))); break; diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index 1b5116e6..494cecc4 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -642,8 +642,11 @@ $lang['admin']['quarantine_retention_size'] = "Rückhaltungen pro Mailbox: