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

@@ -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>