This commit is contained in:
Tudor Georgescu 2017-04-09 22:47:35 +01:00
commit 6ebb216cf2
46 changed files with 1448 additions and 918 deletions

2
.gitignore vendored
View File

@ -4,7 +4,7 @@ data/conf/dovecot/dovecot-master.passwd
mailcow.conf mailcow.conf
mailcow.conf_backup mailcow.conf_backup
data/conf/nginx/listen*active data/conf/nginx/listen*active
data/conf/nginx/server_name*active data/conf/nginx/server_name.active
data/conf/postfix/sql data/conf/postfix/sql
data/conf/dovecot/sql data/conf/dovecot/sql
data/web/inc/vars.local.inc.php data/web/inc/vars.local.inc.php

View File

@ -6,6 +6,8 @@ sed -i "/^\$DBUSER/c\\\$DBUSER='${DBUSER}';" /usr/local/bin/imapsync_cron.pl
sed -i "/^\$DBPASS/c\\\$DBPASS='${DBPASS}';" /usr/local/bin/imapsync_cron.pl sed -i "/^\$DBPASS/c\\\$DBPASS='${DBPASS}';" /usr/local/bin/imapsync_cron.pl
sed -i "/^\$DBNAME/c\\\$DBNAME='${DBNAME}';" /usr/local/bin/imapsync_cron.pl sed -i "/^\$DBNAME/c\\\$DBNAME='${DBNAME}';" /usr/local/bin/imapsync_cron.pl
[[ ! -d /etc/dovecot/sql/ ]] && mkdir -p /etc/dovecot/sql/
# Set Dovecot sql config parameters, escape " in db password # Set Dovecot sql config parameters, escape " in db password
DBPASS=$(echo ${DBPASS} | sed 's/"/\\"/g') DBPASS=$(echo ${DBPASS} | sed 's/"/\\"/g')

View File

@ -81,7 +81,7 @@ user = ${DBUSER}
password = ${DBPASS} password = ${DBPASS}
hosts = mysql hosts = mysql
dbname = ${DBNAME} dbname = ${DBNAME}
query = SELECT goto FROM alias WHERE address='%s' AND active='1' AND domain IN(SELECT domain FROM domain WHERE domain='%d' AND active='1') UNION SELECT logged_in_as FROM sender_acl WHERE send_as='@%d' OR send_as='%s' OR send_as IN ( SELECT CONCAT ('@',target_domain) FROM alias_domain WHERE alias_domain = '%d') OR send_as IN ( SELECT CONCAT ('%u','@',target_domain) FROM alias_domain WHERE alias_domain = '%d' ) AND logged_in_as NOT IN (SELECT goto FROM alias WHERE address='%s') UNION SELECT username FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain) AND mailbox.active ='1' AND alias_domain.active='1' query = SELECT goto FROM alias WHERE address='%s' AND active='1' AND (domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1') OR domain in (SELECT target_domain FROM alias_domain WHERE alias_domain='%d' AND active='1')) UNION SELECT logged_in_as FROM sender_acl WHERE send_as='@%d' OR send_as='%s' OR send_as IN ( SELECT CONCAT ('@',target_domain) FROM alias_domain WHERE alias_domain = '%d') OR send_as IN ( SELECT CONCAT ('%u','@',target_domain) FROM alias_domain WHERE alias_domain = '%d' ) AND logged_in_as NOT IN (SELECT goto FROM alias WHERE address='%s') UNION SELECT username FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain) AND mailbox.active ='1' AND alias_domain.active='1'
EOF EOF
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf

View File

@ -30,8 +30,6 @@ cat <<EOF > /var/lib/sogo/GNUstep/Defaults/sogod.plist
<string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_cache_folder</string> <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_cache_folder</string>
<key>OCSEMailAlarmsFolderURL</key> <key>OCSEMailAlarmsFolderURL</key>
<string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_alarms_folder</string> <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_alarms_folder</string>
<key>DomainFieldName</key>
<string>domain</string>
<key>OCSFolderInfoURL</key> <key>OCSFolderInfoURL</key>
<string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_folder_info</string> <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_folder_info</string>
<key>OCSSessionsFolderURL</key> <key>OCSSessionsFolderURL</key>
@ -63,6 +61,8 @@ while read line
</array> </array>
<key>KindFieldName</key> <key>KindFieldName</key>
<string>kind</string> <string>kind</string>
<key>DomainFieldName</key>
<string>domain</string>
<key>MultipleBookingsFieldName</key> <key>MultipleBookingsFieldName</key>
<string>multiple_bookings</string> <string>multiple_bookings</string>
<key>canAuthenticate</key> <key>canAuthenticate</key>

View File

@ -131,6 +131,9 @@ namespace inbox {
auto = subscribe auto = subscribe
special_use = \Junk special_use = \Junk
} }
mailbox "Junk-E-mail" {
special_use = \Junk
}
mailbox "Junk E-mail" { mailbox "Junk E-mail" {
special_use = \Junk special_use = \Junk
} }

View File

@ -1,15 +0,0 @@
connect = "host=mysql dbname=mailcow user=mailcow password=mysafepasswd"
map {
pattern = priv/quota/storage
table = quota2
username_field = username
value_field = bytes
}
map {
pattern = priv/quota/messages
table = quota2
username_field = username
value_field = messages
}

View File

@ -1,6 +0,0 @@
driver = mysql
connect = "host=mysql dbname=mailcow user=mailcow password=mysafepasswd"
default_pass_scheme = SSHA256
password_query = SELECT password FROM mailbox WHERE username = '%u' AND domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1')
user_query = SELECT CONCAT('maildir:/var/vmail/',maildir) AS mail, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
iterate_query = SELECT username FROM mailbox WHERE active='1';

View File

@ -1 +0,0 @@
server_name logs.servercow.de autodiscover.* autoconfig.*;

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT DISTINCT CASE WHEN '%d' IN (SELECT domain FROM domain WHERE relay_all_recipients=1 AND domain='%d' AND backupmx=1) THEN '%s' ELSE (SELECT goto FROM alias WHERE address='%s' AND active='1') END AS result;

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.address WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_in = '1' AND mailbox.active = '1'), 'reject_plaintext_session', 'DUNNO') AS 'tls_enforce_in';

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.address WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_out = '1' AND mailbox.active = '1'), 'smtp_enforced_tls:', 'DUNNO') AS 'tls_enforce_out';

View File

@ -1,6 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT username FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT goto FROM alias WHERE address='%s' AND active='1';

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT alias_domain from alias_domain WHERE alias_domain='%s' AND active='1' UNION SELECT domain FROM domain WHERE domain='%s' AND active = '1' AND backupmx = '0'

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '1' AND active = '1'

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT goto FROM alias WHERE address='%s' AND active='1' AND domain IN(SELECT domain FROM domain WHERE domain='%d' AND active='1') UNION SELECT logged_in_as FROM sender_acl WHERE send_as='@%d' OR send_as='%s' OR send_as IN ( SELECT CONCAT ('@',target_domain) FROM alias_domain WHERE alias_domain = '%d') OR send_as IN ( SELECT CONCAT ('%u','@',target_domain) FROM alias_domain WHERE alias_domain = '%d' ) AND logged_in_as NOT IN (SELECT goto FROM alias WHERE address='%s') UNION SELECT username FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain) AND mailbox.active ='1' AND alias_domain.active='1'

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT goto FROM spamalias WHERE address='%s' AND validity >= UNIX_TIMESTAMP()

View File

@ -12,16 +12,23 @@ $opt = [
try { try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt); $pdo = new PDO($dsn, $database_user, $database_pass, $opt);
$stmt = $pdo->query("SELECT `username` FROM `mailbox` WHERE `wants_tagged_subject` = '1'"); $stmt = $pdo->query("SELECT `username` FROM `mailbox` WHERE `wants_tagged_subject` = '1'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $rows_a = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) { while ($row_a = array_shift($rows_a)) {
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` REGEXP :username AND goto != `address` AND `address` NOT LIKE '@%'");
$stmt->execute(array(':username' => '(^|,)'.$row_a['username'].'($|,)'));
$rows_a_a = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row_a_a = array_shift($rows_a_a)) {
echo strtolower(trim($row_a_a['address'])) . PHP_EOL;
}
$has_object = 1; $has_object = 1;
echo strtolower(trim($row['username'])) . PHP_EOL; echo strtolower(trim($row_a['username'])) . PHP_EOL;
} }
$stmt = $pdo->query("SELECT CONCAT(mailbox.local_part, '@', alias_domain.alias_domain) as `tag_ad` FROM `mailbox` INNER JOIN `alias_domain` ON mailbox.domain = alias_domain.target_domain WHERE mailbox.wants_tagged_subject='1';"); $stmt = $pdo->query("SELECT CONCAT(`mailbox`.`local_part`, '@', `alias_domain`.`alias_domain`) AS `tag_ad` FROM `mailbox`
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); INNER JOIN `alias_domain` ON `mailbox`.`domain` = `alias_domain`.`target_domain` WHERE `mailbox`.`wants_tagged_subject` = '1';");
while ($row = array_shift($rows)) { $rows_b = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row_b = array_shift($rows_b)) {
$has_object = 1; $has_object = 1;
echo strtolower(trim($row['tag_ad'])) . PHP_EOL; echo strtolower(trim($row_b['tag_ad'])) . PHP_EOL;
} }
if ($has_object == 0) { if ($has_object == 0) {
echo "dummy@domain.local"; echo "dummy@domain.local";

View File

@ -0,0 +1,18 @@
rates {
# Limit for all mail per recipient (burst 100, rate 2 per minute)
to = [100, 0.033333333];
# Limit for all mail per one source ip (burst 30, rate 1.5 per minute)
to_ip = [30, 0.025];
# Limit for all mail per one source ip and from address (burst 20, rate 1 per minute)
to_ip_from = [20, 0.01666666667];
# Limit for all bounce mail (burst 10, rate 2 per hour)
bounce_to = [10, 0.000555556];
# Limit for bounce mail per one source ip (burst 5, rate 1 per hour)
bounce_to_ip = [5, 0.000277778];
# Limit for all mail per authenticated user (burst 20, rate 1 per minute)
user = [20, 0.01666666667];
}
# If symbol is specified, then it is inserted instead of setting result
#symbol = "R_RATELIMIT";
whitelisted_rcpts = "postmaster,mailer-daemon";
max_rcpt = 5;

View File

@ -1 +1,2 @@
# rspamd.conf.local # rspamd.conf.local
history_redis {}

View File

@ -23,40 +23,57 @@ auth_domain_map = rspamd_config:add_map({
description = 'Map of domains we are authoritative for' description = 'Map of domains we are authoritative for'
}) })
rspamd_config.ADD_DELIMITER_TAG = { rspamd_config:register_symbol({
name = 'TAG_MOO',
type = 'postfilter',
callback = function(task) callback = function(task)
local tag = nil
local util = require("rspamd_util") local util = require("rspamd_util")
local rspamd_logger = require "rspamd_logger" local rspamd_logger = require "rspamd_logger"
local user_tagged = task:get_recipients(2)[1]['user']
local domain = task:get_recipients(1)[1]['domain'] local tagged_rcpt = task:get_symbol("TAGGED_RCPT")
local user, tag = user_tagged:match("([^+]+)+(.*)") local user = task:get_recipients(0)[1]['user']
local domain = task:get_recipients(0)[1]['domain']
local rcpt = user .. '@' .. domain
local authdomain = auth_domain_map:get_key(domain) local authdomain = auth_domain_map:get_key(domain)
if tag and authdomain then if tagged_rcpt then
rspamd_logger.infox("domain: %1, tag: %2", domain, tag) local tag = tagged_rcpt[1].options[1]
local user_untagged = user .. '@' .. domain rspamd_logger.infox("found tag: %s", tag)
rspamd_logger.infox("querying tag settings for user %1", user_untagged) local action = task:get_metric_action('default')
if modify_subject_map:get_key(user_untagged) then rspamd_logger.infox("metric action now: %s", action)
rspamd_logger.infox("found user in map for subject rewrite")
local sbj = task:get_header('Subject') if action ~= 'no action' and action ~= 'greylist' then
new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?=' rspamd_logger.infox("skipping tag handler for action: %s", action)
task:set_rmilter_reply({ task:set_metric_action('default', action)
remove_headers = {['Subject'] = 1}, return true
add_headers = {['Subject'] = new_sbj}
})
else
rspamd_logger.infox("add X-Moo-Tag header")
task:set_rmilter_reply({
add_headers = {['X-Moo-Tag'] = 'YES'}
})
end end
else
rspamd_logger.infox("skip delimiter handling for untagged message or authenticated user") if authdomain then
rspamd_logger.infox("found mailcow domain %s", domain)
rspamd_logger.infox("querying tag settings for user %s", rcpt)
if modify_subject_map:get_key(rcpt) then
rspamd_logger.infox("user wants subject modified for tagged mail")
local sbj = task:get_header('Subject')
new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?='
task:set_rmilter_reply({
remove_headers = {['Subject'] = 1},
add_headers = {['Subject'] = new_sbj}
})
else
rspamd_logger.infox("Add X-Moo-Tag header")
task:set_rmilter_reply({
add_headers = {['X-Moo-Tag'] = 'YES'}
})
end
else
rspamd_logger.infox("skip delimiter handling for unknown domain")
end
return false
end end
return false end,
end priority = 10
} })
rspamd_config.MRAPTOR = { rspamd_config.MRAPTOR = {
callback = function(task) callback = function(task)

View File

@ -292,7 +292,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] ==
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="port1">Port</label> <label class="control-label col-sm-2" for="port1"><?=$lang['add']['port'];?></label>
<div class="col-sm-10"> <div class="col-sm-10">
<input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="143" required> <input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="143" required>
</div> </div>

View File

@ -6,7 +6,7 @@ header("Content-Type: application/xml");
<?='<?xml version="1.0"?>';?> <?='<?xml version="1.0"?>';?>
<clientConfig version="1.1"> <clientConfig version="1.1">
<emailProvider id="<?=$mailcow_hostname;?>"> <emailProvider id="<?=$mailcow_hostname;?>">
<domain>%EMAILDOMAIN%</domain>
<displayName>A mailcow mail server</displayName> <displayName>A mailcow mail server</displayName>
<displayShortName>mail server</displayShortName> <displayShortName>mail server</displayShortName>

File diff suppressed because one or more lines are too long

View File

@ -44,3 +44,9 @@
} }
} }
pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;} pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;}
.footable-sortable {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

View File

@ -1467,7 +1467,7 @@ function user_get_alias_details($username) {
try { try {
$data['address'] = $username; $data['address'] = $username;
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `aliases` FROM `alias` $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `aliases` FROM `alias`
WHERE `goto` LIKE :username_goto WHERE `goto` REGEXP :username_goto
AND `address` NOT LIKE '@%' AND `address` NOT LIKE '@%'
AND `address` != :username_address"); AND `address` != :username_address");
$stmt->execute(array(':username_goto' => '(^|,)'.$username.'($|,)', ':username_address' => $username)); $stmt->execute(array(':username_goto' => '(^|,)'.$username.'($|,)', ':username_address' => $username));
@ -1495,8 +1495,8 @@ function user_get_alias_details($username) {
while ($row = array_shift($run)) { while ($row = array_shift($run)) {
$data['aliases_send_as_all'] = $row['send_as']; $data['aliases_send_as_all'] = $row['send_as'];
} }
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') as `address` FROM `alias` WHERE `goto` LIKE :username AND `address` LIKE '@%';"); $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') as `address` FROM `alias` WHERE `goto` REGEXP :username AND `address` LIKE '@%';");
$stmt->execute(array(':username' => '%' . $username . '%')); $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC); $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) { while ($row = array_shift($run)) {
$data['is_catch_all'] = $row['address']; $data['is_catch_all'] = $row['address'];
@ -3320,7 +3320,7 @@ function mailbox_edit_alias_domain($postarray) {
$stmt = $pdo->prepare("UPDATE `alias_domain` SET $stmt = $pdo->prepare("UPDATE `alias_domain` SET
`alias_domain` = :alias_domain, `alias_domain` = :alias_domain,
`active` = :active, `active` = :active,
`modified` = :modified, `modified` = :modified
WHERE `alias_domain` = :alias_domain_now"); WHERE `alias_domain` = :alias_domain_now");
$stmt->execute(array( $stmt->execute(array(
':alias_domain' => $alias_domain, ':alias_domain' => $alias_domain,
@ -4422,7 +4422,7 @@ function mailbox_get_resource_details($resource) {
$resourcedata['name'] = $row['username']; $resourcedata['name'] = $row['username'];
$resourcedata['kind'] = $row['kind']; $resourcedata['kind'] = $row['kind'];
$resourcedata['multiple_bookings'] = $row['multiple_bookings']; $resourcedata['multiple_bookings'] = $row['multiple_bookings'];
$resourcedata['multiple_bookings_int'] = $row['multiple_bookings']; $resourcedata['multiple_bookings_int'] = $row['multiple_bookings_int'];
$resourcedata['description'] = $row['name']; $resourcedata['description'] = $row['name'];
$resourcedata['active'] = $row['active']; $resourcedata['active'] = $row['active'];
$resourcedata['active_int'] = $row['active_int']; $resourcedata['active_int'] = $row['active_int'];
@ -4878,7 +4878,7 @@ function mailbox_get_sender_acl_handles($mailbox) {
try { try {
// Fixed addresses // Fixed addresses
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` LIKE :goto AND `address` NOT LIKE '@%'"); $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` REGEXP :goto AND `address` NOT LIKE '@%'");
$stmt->execute(array(':goto' => '(^|,)'.$mailbox.'($|,)')); $stmt->execute(array(':goto' => '(^|,)'.$mailbox.'($|,)'));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) { while ($row = array_shift($rows)) {

View File

@ -15,6 +15,7 @@
<link rel="stylesheet" href="/css/bootstrap-select.min.css"> <link rel="stylesheet" href="/css/bootstrap-select.min.css">
<link rel="stylesheet" href="/css/bootstrap-slider.min.css"> <link rel="stylesheet" href="/css/bootstrap-slider.min.css">
<link rel="stylesheet" href="/css/bootstrap-switch.min.css"> <link rel="stylesheet" href="/css/bootstrap-switch.min.css">
<link rel="stylesheet" href="/css/footable.bootstrap.min.css">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700&subset=latin,latin-ext"> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700&subset=latin,latin-ext">
<link rel="stylesheet" href="/inc/languages.min.css"> <link rel="stylesheet" href="/inc/languages.min.css">
<link rel="stylesheet" href="/css/mailcow.css"> <link rel="stylesheet" href="/css/mailcow.css">
@ -48,6 +49,7 @@
<li <?=($_SESSION['mailcow_locale'] == 'es') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "es"))) ?>"><span class="lang-xs lang-lbl-full" lang="es"></span></a></li> <li <?=($_SESSION['mailcow_locale'] == 'es') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "es"))) ?>"><span class="lang-xs lang-lbl-full" lang="es"></span></a></li>
<li <?=($_SESSION['mailcow_locale'] == 'nl') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "nl"))) ?>"><span class="lang-xs lang-lbl-full" lang="nl"></span></a></li> <li <?=($_SESSION['mailcow_locale'] == 'nl') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "nl"))) ?>"><span class="lang-xs lang-lbl-full" lang="nl"></span></a></li>
<li <?=($_SESSION['mailcow_locale'] == 'pt') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "pt"))) ?>"><span class="lang-xs lang-lbl-full" lang="pt"></span></a></li> <li <?=($_SESSION['mailcow_locale'] == 'pt') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "pt"))) ?>"><span class="lang-xs lang-lbl-full" lang="pt"></span></a></li>
<li <?=($_SESSION['mailcow_locale'] == 'ru') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "ru"))) ?>"><span class="lang-xs lang-lbl-full" lang="ru"></span></a></li>
</ul> </ul>
</li> </li>
<?php <?php

View File

@ -26,8 +26,7 @@ require_once 'inc/lib/Yubico.php';
// U2F API // U2F API
require_once 'inc/lib/U2F.php'; require_once 'inc/lib/U2F.php';
$scheme = isset($_SERVER['HTTPS']) ? "https://" : "http://"; $u2f = new u2flib_server\U2F('https://' . $_SERVER['SERVER_NAME']);
$u2f = new u2flib_server\U2F($scheme . $_SERVER['HTTP_HOST']);
// PDO // PDO
$dsn = "$database_type:host=$database_host;dbname=$database_name"; $dsn = "$database_type:host=$database_host;dbname=$database_name";
@ -70,6 +69,10 @@ if (isset($_COOKIE['language'])) {
$_SESSION['mailcow_locale'] = 'pt'; $_SESSION['mailcow_locale'] = 'pt';
setcookie('language', 'pt'); setcookie('language', 'pt');
break; break;
case "ru":
$_SESSION['mailcow_locale'] = 'ru';
setcookie('language', 'ru');
break;
} }
} }
if (isset($_GET['lang'])) { if (isset($_GET['lang'])) {
@ -94,6 +97,10 @@ if (isset($_GET['lang'])) {
$_SESSION['mailcow_locale'] = 'pt'; $_SESSION['mailcow_locale'] = 'pt';
setcookie('language', 'pt'); setcookie('language', 'pt');
break; break;
case "ru":
$_SESSION['mailcow_locale'] = 'ru';
setcookie('language', 'ru');
break;
} }
} }
require_once 'lang/lang.en.php'; require_once 'lang/lang.en.php';

View File

@ -26,7 +26,7 @@ $FORM_ACTION = "previous";
$MC_DKIM_TXTS = "/data/dkim/txt"; $MC_DKIM_TXTS = "/data/dkim/txt";
$MC_DKIM_KEYS = "/data/dkim/keys"; $MC_DKIM_KEYS = "/data/dkim/keys";
// Change default language, "en", "pt", "de" or "nl" // Change default language, "en", "es" "pt", "de", "ru" or "nl"
$DEFAULT_LANG = "en"; $DEFAULT_LANG = "en";
// Change theme (default: lumen) // Change theme (default: lumen)
@ -37,4 +37,21 @@ $DEFAULT_THEME = "lumen";
// Password complexity as regular expression // Password complexity as regular expression
$PASSWD_REGEP = '.{4,}'; $PASSWD_REGEP = '.{4,}';
// mailcow Apps - buttons on login screen
$MAILCOW_APPS = array(
array(
'name' => 'SOGo',
'link' => '/SOGo/'
),
// array(
// 'name' => 'Roundcube',
// 'link' => '/rc/'
// ),
);
// Rows until pagination begins
$PAGINATION_SIZE = 10;
?> ?>

View File

@ -29,7 +29,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<label class="sr-only" for="login_user"><?=$lang['login']['username'];?></label> <label class="sr-only" for="login_user"><?=$lang['login']['username'];?></label>
<div class="input-group"> <div class="input-group">
<div class="input-group-addon"><i class="glyphicon glyphicon-user"></i></div> <div class="input-group-addon"><i class="glyphicon glyphicon-user"></i></div>
<input name="login_user" autocorrect="off" autocapitalize="none" type="name" id="login_user" class="form-control" placeholder="<?=$lang['login']['username'];?>" required="" autofocus=""> <input name="login_user" autocorrect="off" autocapitalize="none" type="text" id="login_user" class="form-control" placeholder="<?=$lang['login']['username'];?>" required="" autofocus="">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -51,6 +51,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<li <?=($_SESSION['mailcow_locale'] == 'es') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "es"))) ?>"><span class="lang-xs lang-lbl-full" lang="es"></span></a></li> <li <?=($_SESSION['mailcow_locale'] == 'es') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "es"))) ?>"><span class="lang-xs lang-lbl-full" lang="es"></span></a></li>
<li <?=($_SESSION['mailcow_locale'] == 'nl') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "nl"))) ?>"><span class="lang-xs lang-lbl-full" lang="nl"></span></a></li> <li <?=($_SESSION['mailcow_locale'] == 'nl') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "nl"))) ?>"><span class="lang-xs lang-lbl-full" lang="nl"></span></a></li>
<li <?=($_SESSION['mailcow_locale'] == 'pt') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "pt"))) ?>"><span class="lang-xs lang-lbl-full" lang="pt"></span></a></li> <li <?=($_SESSION['mailcow_locale'] == 'pt') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "pt"))) ?>"><span class="lang-xs lang-lbl-full" lang="pt"></span></a></li>
<li <?=($_SESSION['mailcow_locale'] == 'ru') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "ru"))) ?>"><span class="lang-xs lang-lbl-full" lang="ru"></span></a></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -63,7 +64,11 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
endif; endif;
?> ?>
<legend>mailcow Apps</legend> <legend>mailcow Apps</legend>
<a href="/SOGo/" role="button" class="btn btn-lg btn-default"><?=$lang['start']['start_sogo'];?></a> <?php
foreach ($MAILCOW_APPS as $app) {
echo '<a href="' . $app['link'] . '" role="button" class="btn btn-lg btn-default">' . $app['name'] . '</a>&nbsp;';
}
?>
</div> </div>
</div> </div>
</div> </div>

10
data/web/js/footable.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,54 +1,256 @@
$(document).ready(function() { $(document).ready(function() {
// Show element counter for tables
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
var rowCountDomainAlias = $('#domainaliastable >tbody >#data').length; function humanFileSize(bytes) {
var rowCountDomain = $('#domaintable >tbody >#data').length; if(Math.abs(bytes) < 1024) {
var rowCountMailbox = $('#mailboxtable >tbody >#data').length; return bytes + ' B';
var rowCountAlias = $('#aliastable >tbody >#data').length; }
var rowCountResource = $('#resourcetable >tbody >#data').length; var units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
$("#numRowsDomainAlias").text(rowCountDomainAlias); var u = -1;
$("#numRowsDomain").text(rowCountDomain); do {
$("#numRowsMailbox").text(rowCountMailbox); bytes /= 1024;
$("#numRowsAlias").text(rowCountAlias); ++u;
$("#numRowsResource").text(rowCountResource); } while(Math.abs(bytes) >= 1024 && u < units.length - 1);
return bytes.toFixed(1)+' '+units[u];
}
// Filter table function $.ajax({
$.fn.extend({ dataType: 'json',
filterTable: function(){ url: '/json_api.php?action=domain_table_data',
return this.each(function(){ jsonp: false,
$(this).on('keyup', function(e){ error: function () {
var $this = $(this), alert('Cannot draw domain table');
search = $this.val().toLowerCase(), },
target = $this.attr('data-filters'), success: function (data) {
$target = $(target), $.each(data, function (i, item) {
$rows = $target.find('tbody #data'); item.aliases = item.aliases_in_domain + " / " + item.max_num_aliases_for_domain;
$target.find('tbody .filterTable_no_results').remove(); item.mailboxes = item.mboxes_in_domain + " / " + item.max_num_mboxes_for_domain;
if(search == '') { item.quota = humanFileSize(item.quota_used_in_domain) + " / " + humanFileSize(item.max_quota_for_domain);
$target.find('tbody #no-data').show(); item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
$rows.show(); if (role == "admin") {
} else { item.action = '<div class="btn-group">' +
$target.find('tbody #no-data').hide(); '<a href="/edit.php?domain=' + encodeURI(item.domain_name) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
$rows.each(function(){ '<a href="/delete.php?domain=' + encodeURI(item.domain_name) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
var $this = $(this); '</div>';
$this.text().toLowerCase().indexOf(search) === -1 ? $this.hide() : $this.show(); }
}) else {
if($target.find('tbody #data:visible').size() === 0) { item.action = '<div class="btn-group">' +
var col_count = $target.find('#data').first().find('td').size(); '<a href="/edit.php?domain=' + encodeURI(item.domain_name) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
var no_results = $('<tr class="filterTable_no_results"><td colspan="100%">-</td></tr>') '</div>';
$target.find('tbody').prepend(no_results); }
} });
} $('#domain_table').footable({
}); "columns": [
}); {"sorted": true,"name":"domain_name","title":lang.domain,"style":{"width":"250px"}},
} {"name":"aliases","title":lang.aliases,"breakpoints":"xs sm"},
}); {"name":"mailboxes","title":lang.mailboxes},
$('[data-action="filter"]').filterTable(); {"name":"quota","title":lang.domain_quota},
$('.container').on('click', '.panel-heading span.filter', function(e){ {"name":"max_quota_for_mbox","title":lang.mailbox_quota,"breakpoints":"xs sm"},
var $this = $(this), {"name":"backupmx","filterable": false,"style":{"maxWidth":"120px","width":"120px"},"title":lang.backup_mx,"breakpoints":"xs sm"},
$panel = $this.parents('.panel'); {"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
$panel.find('.panel-body').slideToggle("fast"); {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
if($this.css('display') != 'none') { ],
$panel.find('.panel-body input').focus(); "rows": data,
} "empty": lang.empty,
}); "paging": {
"enabled": true,
"limit": 5,
"size": pagination_size
},
"filtering": {
"enabled": true,
"position": "left",
"placeholder": lang.filter_table
},
"sorting": {
"enabled": true
}
});
}
});
$.ajax({
dataType: 'json',
url: '/json_api.php?action=mailbox_table_data',
jsonp: false,
error: function () {
alert('Cannot draw mailbox table');
},
success: function (data) {
$.each(data, function (i, item) {
item.quota = humanFileSize(item.quota_used) + " / " + humanFileSize(item.quota);
item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
if (role == "admin") {
item.action = '<div class="btn-group">' +
'<a href="/edit.php?mailbox=' + encodeURI(item.username) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
'<a href="/delete.php?mailbox=' + encodeURI(item.username) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'<a href="/index.php?duallogin=' + encodeURI(item.username) + '" class="btn btn-xs btn-success"><span class="glyphicon glyphicon-user"></span> Login</a>' +
'</div>';
}
else {
item.action = '<div class="btn-group">' +
'<a href="/edit.php?mailbox=' + encodeURI(item.username) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
'<a href="/delete.php?mailbox=' + encodeURI(item.username) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'</div>';
}
item.in_use = '<div class="progress">' +
'<div class="progress-bar progress-bar-' + item.percent_class + ' role="progressbar" aria-valuenow="' + item.percent_in_use + '" aria-valuemin="0" aria-valuemax="100" ' +
'style="min-width:2em;width:' + item.percent_in_use + '%">' + item.percent_in_use + '%' + '</div></div>';
});
$('#mailbox_table').footable({
"columns": [
{"sorted": true,"name":"username","title":lang.username,"style":{"width":"250px"}},
{"name":"name","title":lang.fname,"breakpoints":"xs sm"},
{"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
{"name":"quota","title":lang.domain_quota},
{"name":"spam_aliases","filterable": false,"title":lang.spam_aliases,"breakpoints":"xs sm"},
{"name":"in_use","filterable": false,"type":"html","title":lang.in_use},
{"name":"messages","filterable": false,"style":{"width":"90px"},"title":lang.msg_num,"breakpoints":"xs sm"},
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","width":"290px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
],
"empty": lang.empty,
"rows": data,
"paging": {
"enabled": true,
"limit": 5,
"size": pagination_size
},
"filtering": {
"enabled": true,
"position": "left",
"placeholder": lang.filter_table
},
"sorting": {
"enabled": true
}
});
}
});
$.ajax({
dataType: 'json',
url: '/json_api.php?action=resource_table_data',
jsonp: false,
error: function () {
alert('Cannot draw resource table');
},
success: function (data) {
$.each(data, function (i, item) {
item.action = '<div class="btn-group">' +
'<a href="/edit.php?resource=' + encodeURI(item.name) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
'<a href="/delete.php?resource=' + encodeURI(item.name) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'</div>';
});
$('#resources_table').footable({
"columns": [
{"sorted": true,"name":"description","title":lang.description,"style":{"width":"250px"}},
{"name":"kind","title":lang.kind},
{"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
{"name":"multiple_bookings","filterable": false,"style":{"maxWidth":"120px","width":"120px"},"title":lang.multiple_bookings,"breakpoints":"xs sm"},
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
],
"empty": lang.empty,
"rows": data,
"paging": {
"enabled": true,
"limit": 5,
"size": pagination_size
},
"filtering": {
"enabled": true,
"position": "left",
"placeholder": lang.filter_table
},
"sorting": {
"enabled": true
}
});
}
});
$.ajax({
dataType: 'json',
url: '/json_api.php?action=domain_alias_table_data',
jsonp: false,
error: function () {
alert('Cannot draw alias domain table');
},
success: function (data) {
$.each(data, function (i, item) {
item.action = '<div class="btn-group">' +
'<a href="/edit.php?aliasdomain=' + encodeURI(item.alias_domain) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
'<a href="/delete.php?aliasdomain=' + encodeURI(item.alias_domain) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'</div>';
});
$('#aliasdomain_table').footable({
"columns": [
{"sorted": true,"name":"alias_domain","title":lang.alias,"style":{"width":"250px"}},
{"name":"target_domain","title":lang.target_domain},
{"name":"active","filterable": false,"style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
],
"empty": lang.empty,
"rows": data,
"paging": {
"enabled": true,
"limit": 5,
"size": pagination_size
},
"filtering": {
"enabled": true,
"position": "left",
"placeholder": lang.filter_table
},
"sorting": {
"enabled": true
}
});
}
});
$.ajax({
dataType: 'json',
url: '/json_api.php?action=alias_table_data',
jsonp: false,
error: function () {
alert('Cannot draw alias table');
},
success: function (data) {
$.each(data, function (i, item) {
if (item.is_catch_all == 1) {
item.address = '<div class="label label-default">Catch-All</div> ' + item.address;
}
item.action = '<div class="btn-group">' +
'<a href="/edit.php?alias=' + encodeURI(item.address) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> ' + lang.edit + '</a>' +
'<a href="/delete.php?alias=' + encodeURI(item.address) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'</div>';
});
$('#alias_table').footable({
"columns": [
{"sorted": true,"name":"address","title":lang.alias,"style":{"width":"250px"}},
{"name":"goto","title":lang.target_address},
{"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
{"name":"active","filterable": false,"style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
],
"empty": lang.empty,
"rows": data,
"paging": {
"enabled": true,
"limit": 5,
"size": pagination_size
},
"filtering": {
"enabled": true,
"position": "left",
"placeholder": lang.filter_table
},
"sorting": {
"enabled": true
}
});
}
});
}); });

View File

@ -2,13 +2,119 @@
require_once 'inc/prerequisites.inc.php'; require_once 'inc/prerequisites.inc.php';
error_reporting(E_ALL); error_reporting(E_ALL);
if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) { if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) {
if ($_GET['action'] && $_GET['object']) { if (isset($_GET['action'])) {
$action = $_GET['action']; $action = $_GET['action'];
$object = $_GET['object'];
switch ($action) { switch ($action) {
case "domain_table_data":
$domains = mailbox_get_domains();
if (!empty($domains)) {
foreach ($domains as $domain) {
$data[] = mailbox_get_domain_details($domain);
}
if (!isset($data) || empty($data)) {
echo '{}';
}
else {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
}
else {
echo '{}';
}
break;
case "mailbox_table_data":
$domains = mailbox_get_domains();
if (!empty($domains)) {
foreach ($domains as $domain) {
$mailboxes = mailbox_get_mailboxes($domain);
if (!empty($mailboxes)) {
foreach ($mailboxes as $mailbox) {
$data[] = mailbox_get_mailbox_details($mailbox);
}
}
}
if (!isset($data) || empty($data)) {
echo '{}';
}
else {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
}
else {
echo '{}';
}
break;
case "resource_table_data":
$domains = mailbox_get_domains();
if (!empty($domains)) {
foreach ($domains as $domain) {
$resources = mailbox_get_resources($domain);
if (!empty($resources)) {
foreach ($resources as $resource) {
$data[] = mailbox_get_resource_details($resource);
}
}
}
if (!isset($data) || empty($data)) {
echo '{}';
}
else {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
}
else {
echo '{}';
}
break;
case "domain_alias_table_data":
$domains = mailbox_get_domains();
if (!empty($domains)) {
foreach ($domains as $domain) {
$alias_domains = mailbox_get_alias_domains($domain);
if (!empty($alias_domains)) {
foreach ($alias_domains as $alias_domain) {
$data[] = mailbox_get_alias_domain_details($alias_domain);
}
}
}
if (!isset($data) || empty($data)) {
echo '{}';
}
else {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
}
else {
echo '{}';
}
break;
case "alias_table_data":
$domains = array_merge(mailbox_get_domains(), mailbox_get_alias_domains());
if (!empty($domains)) {
foreach ($domains as $domain) {
$aliases = mailbox_get_aliases($domain);
if (!empty($aliases)) {
foreach ($aliases as $alias) {
$data[] = mailbox_get_alias_details($alias);
}
}
}
if (!isset($data) || empty($data)) {
echo '{}';
}
else {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
}
else {
echo '{}';
}
break;
case "get_mailbox_details": case "get_mailbox_details":
if (!isset($_GET['object'])) { return false; }
$object = $_GET['object'];
$data = mailbox_get_mailbox_details($object); $data = mailbox_get_mailbox_details($object);
if (!$data || empty($data)) { if (!isset($data) || empty($data)) {
echo '{}'; echo '{}';
} }
else { else {
@ -16,8 +122,10 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
} }
break; break;
case "get_domain_details": case "get_domain_details":
if (!isset($_GET['object'])) { return false; }
$object = $_GET['object'];
$data = mailbox_get_domain_details($object); $data = mailbox_get_domain_details($object);
if (!$data || empty($data)) { if (!isset($data) || empty($data)) {
echo '{}'; echo '{}';
} }
else { else {
@ -25,6 +133,8 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
} }
break; break;
case "get_u2f_reg_challenge": case "get_u2f_reg_challenge":
if (!isset($_GET['object'])) { return false; }
$object = $_GET['object'];
if ( if (
($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") ($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin")
&& &&
@ -40,6 +150,8 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
} }
break; break;
case "get_u2f_auth_challenge": case "get_u2f_auth_challenge":
if (!isset($_GET['object'])) { return false; }
$object = $_GET['object'];
if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) { if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) {
$reqs = json_encode($u2f->getAuthenticateData(get_u2f_registrations($object))); $reqs = json_encode($u2f->getAuthenticateData(get_u2f_registrations($object)));
$_SESSION['authReq'] = $reqs; $_SESSION['authReq'] = $reqs;

View File

@ -318,6 +318,7 @@ $lang['edit']['resource'] = 'Ressource';
$lang['add']['syncjob'] = 'Sync-Job erstellen'; $lang['add']['syncjob'] = 'Sync-Job erstellen';
$lang['add']['syncjob_hint'] = 'Passwörter werden unverschlüsselt abgelegt!'; $lang['add']['syncjob_hint'] = 'Passwörter werden unverschlüsselt abgelegt!';
$lang['add']['hostname'] = 'Servername'; $lang['add']['hostname'] = 'Servername';
$lang['add']['port'] = 'Port';
$lang['add']['username'] = 'Benutzername'; $lang['add']['username'] = 'Benutzername';
$lang['add']['enc_method'] = 'Verschlüsselungsmethode'; $lang['add']['enc_method'] = 'Verschlüsselungsmethode';
$lang['add']['maxage'] = 'Maximum age of messages that will be polled from remote (0 = ignore age)'; $lang['add']['maxage'] = 'Maximum age of messages that will be polled from remote (0 = ignore age)';

View File

@ -322,6 +322,7 @@ $lang['edit']['resource'] = 'Resource';
$lang['add']['syncjob'] = 'Add sync job'; $lang['add']['syncjob'] = 'Add sync job';
$lang['add']['syncjob_hint'] = 'Be aware that passwords need to be saved plain-text!'; $lang['add']['syncjob_hint'] = 'Be aware that passwords need to be saved plain-text!';
$lang['add']['hostname'] = 'Hostname'; $lang['add']['hostname'] = 'Hostname';
$lang['add']['port'] = 'Port';
$lang['add']['username'] = 'Username'; $lang['add']['username'] = 'Username';
$lang['add']['enc_method'] = 'Encryption method'; $lang['add']['enc_method'] = 'Encryption method';
$lang['add']['mins_interval'] = 'Polling interval (minutes)'; $lang['add']['mins_interval'] = 'Polling interval (minutes)';
@ -387,7 +388,7 @@ $lang['tfa']['api_register'] = 'mailcow uses the Yubico Cloud API. Please get an
$lang['tfa']['u2f'] = "U2F authentication"; $lang['tfa']['u2f'] = "U2F authentication";
$lang['tfa']['hotp'] = "HOTP authentication"; $lang['tfa']['hotp'] = "HOTP authentication";
$lang['tfa']['totp'] = "TOTP authentication"; $lang['tfa']['totp'] = "TOTP authentication";
$lang['tfa']['none'] = "Deaktiviert"; $lang['tfa']['none'] = "Deactivate";
$lang['tfa']['delete_tfa'] = "Disable TFA"; $lang['tfa']['delete_tfa'] = "Disable TFA";
$lang['tfa']['disable_tfa'] = "Disable TFA until next successful login"; $lang['tfa']['disable_tfa'] = "Disable TFA until next successful login";
$lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field"; $lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field";

View File

@ -299,7 +299,7 @@ $lang['add']['password'] = 'Constraseña:';
$lang['add']['password_repeat'] = 'Confirmación de contraseña (repetir):'; $lang['add']['password_repeat'] = 'Confirmación de contraseña (repetir):';
$lang['add']['previous'] = 'Página anterior'; $lang['add']['previous'] = 'Página anterior';
$lang['add']['restart_sogo_hint'] = '¡Necesitas reiniciar el contenedor del servicio SOGo antes de agregar un nuevo dominio!'; $lang['add']['restart_sogo_hint'] = '¡Necesitas reiniciar el contenedor del servicio SOGo antes de agregar un nuevo dominio!';
$lang['add']['port'] = 'Port';
$lang['login']['title'] = 'Inicio de sesión'; $lang['login']['title'] = 'Inicio de sesión';
$lang['login']['administration'] = 'Administración'; $lang['login']['administration'] = 'Administración';
$lang['login']['administration_details'] = 'Por favor utiliza tu inicio de sesión de Administrador para realizar tareas administrativas.'; $lang['login']['administration_details'] = 'Por favor utiliza tu inicio de sesión de Administrador para realizar tareas administrativas.';

View File

@ -259,6 +259,7 @@ $lang['edit']['previous'] = 'Vorige pagina';
$lang['edit']['unchanged_if_empty'] = 'Leeg laten indien niet veranderd.'; $lang['edit']['unchanged_if_empty'] = 'Leeg laten indien niet veranderd.';
$lang['edit']['dont_check_sender_acl'] = 'Geen zenderverificatie uitvoeren voor domein %s.'; $lang['edit']['dont_check_sender_acl'] = 'Geen zenderverificatie uitvoeren voor domein %s.';
$lang['add']['port'] = 'Port';
$lang['add']['title'] = 'Object toevoegen'; $lang['add']['title'] = 'Object toevoegen';
$lang['add']['domain'] = 'Domein'; $lang['add']['domain'] = 'Domein';
$lang['add']['active'] = 'Actief'; $lang['add']['active'] = 'Actief';

View File

@ -279,6 +279,7 @@ $lang['add']['select_domain'] = 'Selecione um domínio antes';
$lang['add']['password'] = 'Senha:'; $lang['add']['password'] = 'Senha:';
$lang['add']['password_repeat'] = 'Confirmar a senha (repetir):'; $lang['add']['password_repeat'] = 'Confirmar a senha (repetir):';
$lang['add']['previous'] = 'Voltar'; $lang['add']['previous'] = 'Voltar';
$lang['add']['port'] = 'Port';
$lang['login']['title'] = 'Entrar'; $lang['login']['title'] = 'Entrar';
$lang['login']['administration'] = 'Administração'; $lang['login']['administration'] = 'Administração';
$lang['login']['administration_details'] = 'Utilize o login de Administrador para efetuar tarefas de administração.'; $lang['login']['administration_details'] = 'Utilize o login de Administrador para efetuar tarefas de administração.';

450
data/web/lang/lang.ru.php Normal file
View File

@ -0,0 +1,450 @@
<?php
/*
//
// Russian language file
//
*/
$lang['footer']['loading'] = "Пожалуйста, подождите...";
$lang['header']['restart_sogo'] = "Перезагрузить SOGo";
$lang['footer']['restart_sogo'] = "Перезагрузить SOGo";
$lang['footer']['restart_now'] = "Перезагрузить сейчас";
$lang['footer']['restart_sogo_info'] = "Некоторые операции, например при добавлении домена, требуют перезагрузить SOGo, для вступления в силу внесенных изменений.<br /><br /><b>Важно:</b> Перезагрузка может занять некоторое время, дождитесь ее окончания.";
$lang['dkim']['confirm'] = "Вы уверены?";
$lang['danger']['dkim_not_found'] = "DKIM ключ не найден";
$lang['danger']['dkim_remove_failed'] = "Не удается удалить выбранный DKIM ключ";
$lang['danger']['dkim_add_failed'] = "Невозможно добавить данный DKIM ключ";
$lang['danger']['dkim_domain_or_sel_invalid'] = "Недопустимый домен";
$lang['danger']['dkim_key_length_invalid'] = "Недопустимая длина DKIM ключа";
$lang['success']['dkim_removed'] = "DKIM ключ удален";
$lang['success']['dkim_added'] = "DKIM ключ сохранен";
$lang['danger']['access_denied'] = "Доступ запрещен или неверные данные формы";
$lang['danger']['whitelist_from_invalid'] = "Недопустимая запись белого списка";
$lang['danger']['domain_invalid'] = "Недопустимое имя домена";
$lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Максимальная квота превышает квоту домена";
$lang['danger']['object_is_not_numeric'] = "Значение %s не является числовым";
$lang['success']['domain_added'] = "Добавлен домен %s";
$lang['danger']['alias_empty'] = "Псевдоним адрес не может быть пустым";
$lang['danger']['last_key'] = "Невозможно удалить послений ключ";
$lang['danger']['goto_empty'] = "Основной адрес не может быть пустым";
$lang['danger']['policy_list_from_exists'] = "Запись с указанным именем уже существует";
$lang['danger']['policy_list_from_invalid'] = "Запись имеет недопустимый формат";
$lang['danger']['whitelist_exists'] = "Указанная запись уже существует в белом списке";
$lang['danger']['whitelist_from_invalid'] = "Указанная запись белого списка имеет недопустимый формат";
$lang['danger']['alias_invalid'] = "Недопустимый псевдоним адрес";
$lang['danger']['goto_invalid'] = "Неверный основной адрес";
$lang['danger']['alias_domain_invalid'] = "Недопустимый псевдоним домена";
$lang['danger']['target_domain_invalid'] = "Неверный основной домен";
$lang['danger']['object_exists'] = "Объект %s уже существует";
$lang['danger']['domain_exists'] = "Домен %s уже существует";
$lang['danger']['alias_goto_identical'] = "Псевдоним адрес и основной адрес не могут быть одинаковыми";
$lang['danger']['aliasd_targetd_identical'] = "Псевдоним домена и основной домен не могут быть одинаковыми";
$lang['success']['alias_added'] = "Псевдоним адрес(а) был(и) добавлен(ы)";
$lang['success']['alias_modified'] = "Изменения псевдоним адреса сохранены";
$lang['success']['aliasd_modified'] = "Изменения псевдоним домена сохранены";
$lang['success']['mailbox_modified'] = "Изменения почтового ящика %s сохранены";
$lang['success']['resource_modified'] = "Изменения почтового ящика %s сохранены";
$lang['success']['object_modified'] = "Изменения объекта %s сохранены";
$lang['success']['msg_size_saved'] = "Установлен новый максимальный размер письма";
$lang['danger']['aliasd_not_found'] = "Псевдоним домена не найден";
$lang['danger']['targetd_not_found'] = "Основной домен не найден";
$lang['danger']['aliasd_exists'] = "Псевдоним домена уже существует";
$lang['success']['aliasd_added'] = "Добавлен псевдоним домена %s";
$lang['success']['aliasd_modified'] = "Изменения псевдоним домена %s сохранены";
$lang['success']['domain_modified'] = "Изменения домена %s сохранены";
$lang['success']['domain_admin_modified'] = "Изменения администратора домена %s сохранены";
$lang['success']['domain_admin_added'] = "Администратор домена %s добавлен";
$lang['success']['changes_general'] = "Изменения сохранены";
$lang['success']['admin_modified'] = "Изменения администратора сохранены";
$lang['danger']['exit_code_not_null'] = "Ошибка: код ошибки %d";
$lang['danger']['mailbox_not_available'] = "Почтовый ящик недоступен";
$lang['danger']['username_invalid'] = "Нельзя использовать этот логин";
$lang['danger']['password_mismatch'] = "Введенные пароли не совпадают";
$lang['danger']['password_complexity'] = "Пароль не соответствует требованиям";
$lang['danger']['password_empty'] = "Пароль не может быть пустым";
$lang['danger']['login_failed'] = "Введен неверный логин или пароль";
$lang['danger']['mailbox_invalid'] = "Недопустимое имя почтового ящика";
$lang['danger']['description_invalid'] = "Недопустимое описание ресурса";
$lang['danger']['resource_invalid'] = "Недопустимое имя ресурса";
$lang['danger']['mailbox_invalid_suggest'] = "Имя почтового ящика недействительно, возможно вы имели в виду %s?";
$lang['danger']['is_alias'] = "%s уже известен как псевдоним адреса";
$lang['danger']['is_alias_or_mailbox'] = "%s уже известен как псевдоним адреса или почтовый ящик";
$lang['danger']['is_spam_alias'] = "%s уже известен как спам псевдоним адрес";
$lang['danger']['quota_not_0_not_numeric'] = "Размер квоты должен быть больше нуля";
$lang['danger']['domain_not_found'] = "Домен не найден";
$lang['danger']['max_mailbox_exceeded'] = "Превышено максимальное количество почтовых ящиков (%d из %d)";
$lang['danger']['mailbox_quota_exceeded'] = "Квота превышает лимит домена (максимум %d MiB)";
$lang['danger']['mailbox_quota_left_exceeded'] = "Недостаточно свободного места (места осталось: %d MiB)";
$lang['success']['mailbox_added'] = "Почтовый ящик %s добавлен";
$lang['success']['resource_added'] = "Ресурс %s добавлен";
$lang['success']['domain_removed'] = "Домен %s удален";
$lang['success']['alias_removed'] = "Псевдоним адрес %s удален";
$lang['success']['alias_domain_removed'] = "Псевдоним домена %s удален";
$lang['success']['domain_admin_removed'] = "Администратор домена %s удален";
$lang['success']['mailbox_removed'] = "Почтовый ящик %s удален";
$lang['success']['eas_reset'] = "Устройства ActiveSync для пользователя %s были сброшены";
$lang['success']['resource_removed'] = "Ресурс %s удален";
$lang['danger']['max_quota_in_use'] = "Квота почтового ящика должна быть больше или равна %d MiB";
$lang['danger']['domain_quota_m_in_use'] = "Квота домена должна быть больше или равна %s MiB";
$lang['danger']['mailboxes_in_use'] = "Максимальный лимит почтовых ящиков должен быть больше или равен %d";
$lang['danger']['aliases_in_use'] = "Максимальный лимит псевдоним адресов должен быть больше или равен %d";
$lang['danger']['sender_acl_invalid'] = "Недопустимое значение ACL отправителя";
$lang['danger']['domain_not_empty'] = "Нельзя удалить непустой домен";
$lang['warning']['spam_alias_temp_error'] = "Временная ошибка: Не удается добавить спам псевдоним, пожалуйста, попробуй снова позже";
$lang['danger']['spam_alias_max_exceeded'] = "Превышение максимально разрешенных спам псевдонимов";
$lang['danger']['validity_missing'] = "Пожалуйста, назначьте срок действия";
$lang['user']['on'] = "Вкл.";
$lang['user']['off'] = "Выкл.";
$lang['user']['messages'] = "писем"; // "123 messages"
$lang['user']['in_use'] = "Занято";
$lang['user']['user_change_fn'] = "";
$lang['user']['user_settings'] = "Настройки пользователя";
$lang['user']['mailbox_settings'] = "Настройки почтового ящика";
$lang['user']['mailbox_details'] = "Данные почтового ящика";
$lang['user']['change_password'] = "Сменить пароль";
$lang['user']['new_password'] = "Новый пароль";
$lang['user']['save_changes'] = "Сохранить изменения";
$lang['user']['password_now'] = "Текущий пароль";
$lang['user']['new_password_repeat'] = "Повторить пароль";
$lang['user']['new_password_description'] = "Требование: 6 символов, буквы и цифры.";
$lang['user']['did_you_know'] = '<b>Вы знали?</b> You can use tags in your email address ("me+<b>privat</b>@example.com") to move messages to a folder automatically (example: "privat").';
$lang['user']['spam_aliases'] = "Временные псевдонимы электронной почты";
$lang['user']['alias'] = "Псевдоним";
$lang['user']['aliases'] = "Псевдонимы";
$lang['user']['domain_aliases'] = "Адреса псевдонимов домена";
$lang['user']['is_catch_all'] = 'Catch-all for domain/s';
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as user';
$lang['user']['aliases_send_as_all'] = 'Do not check sender access for the following domain(s) and its alias domains';
$lang['user']['alias_create_random'] = "Генерировать случайный псевдоним адрес";
$lang['user']['alias_extend_all'] = "Продлить псевдоним адреса на 1 час";
$lang['user']['alias_valid_until'] = "Действителен до";
$lang['user']['alias_remove_all'] = "Удалить все псевдоним адреса";
$lang['user']['alias_time_left'] = "Осталось времени";
$lang['user']['alias_full_date'] = "d.m.Y, H:i:s T";
$lang['user']['syncjob_full_date'] = "d.m.Y, H:i:s T";
$lang['user']['alias_select_validity'] = "Срок действия";
$lang['user']['sync_jobs'] = "Синхронизировать задания";
$lang['user']['hour'] = "час";
$lang['user']['hours'] = "часов";
$lang['user']['day'] = "день";
$lang['user']['week'] = "неделя";
$lang['user']['weeks'] = "недели";
$lang['user']['spamfilter'] = "Спам-фильтр";
$lang['user']['spamfilter_wl'] = "Белый список";
$lang['user']['spamfilter_wl_desc'] = 'Whitelisted email addresses to <b>never</b> classify as spam. Wildcards maybe used.';
$lang['user']['spamfilter_bl'] = "Черный список";
$lang['user']['spamfilter_bl_desc'] = 'Blacklisted email addresses to <b>always</b> classify as spam and reject. Wildcards maybe used.';
$lang['user']['spamfilter_behavior'] = "Рейтинг";
$lang['user']['spamfilter_table_rule'] = "Правила";
$lang['user']['spamfilter_table_action'] = "Действие";
$lang['user']['spamfilter_table_empty'] = "Нет данных для отображения";
$lang['user']['spamfilter_table_remove'] = "Удалить";
$lang['user']['spamfilter_table_add'] = "Добавить";
$lang['user']['spamfilter_default_score'] = "Оценки спама";
$lang['user']['spamfilter_green'] = 'Green: this message is not spam';
$lang['user']['spamfilter_yellow'] = 'Yellow: this message may be spam, will be tagged as spam and moved to your junk folder';
$lang['user']['spamfilter_red'] = 'Red: This message is spam and will be rejected by the server';
$lang['user']['spamfilter_default_score'] = "Значения по умолчанию";
$lang['user']['spamfilter_hint'] = 'The first value describes the "low spam score", the second represents the "high spam score".';
$lang['user']['spamfilter_table_domain_policy'] = "n/a (domain policy)";
$lang['user']['tls_policy_warning'] = '<strong>Warning:</strong> If you decide to enforce encrypted mail transfer, you may lose emails.<br />Messages to not satisfy the policy will be bounced with a hard fail by the mail system.';
$lang['user']['tls_policy'] = "Настройки TLS шифрования";
$lang['user']['tls_enforce_in'] = "Принудительное TLS входящих";
$lang['user']['tls_enforce_out'] = "Принудительное TLS исходящих";
$lang['user']['no_record'] = "Нет записи";
$lang['user']['misc_settings'] = "Другие настройки профиля";
$lang['user']['misc_delete_profile'] = "Другие настройки профиля";
$lang['user']['tag_handling'] = 'Set handling for tagged mail';
$lang['user']['tag_in_subfolder'] = "В подпапке";
$lang['user']['tag_in_subject'] = "В теме";
$lang['user']['tag_help_explain'] = 'In subfolder: a new subfolder named after the tag will be created below INBOX ("INBOX/Facebook").<br />
In subject: the tags name will be prepended to the mails subject, example: "[Facebook] Meine Neuigkeiten".';
$lang['user']['tag_help_example'] = 'Example for a tagged email address: ich<b>+Facebook</b>@example.org';
$lang['user']['eas_reset'] = "Сбросить кеш ActiveSync устройств";
$lang['user']['eas_reset_now'] = "Сбросить сейчас";
$lang['user']['eas_reset_help'] = 'In many cases a device cache reset will help to recover a broken ActiveSync profile.<br /><b>Attention:</b> All elements will be redownloaded!';
$lang['user']['encryption'] = "Шифрование";
$lang['user']['username'] = "Логин";
$lang['user']['password'] = "Пароль";
$lang['user']['last_run'] = "Последний запуск";
$lang['user']['excludes'] = "Исключает";
$lang['user']['interval'] = "Интервал";
$lang['user']['active'] = "Статус";
$lang['user']['action'] = "Действия";
$lang['user']['edit'] = "Изменить";
$lang['user']['remove'] = "Удалить";
$lang['user']['delete_now'] = "Удалить сейчас";
$lang['user']['create_syncjob'] = "Создание новой задачи синхронизации";
$lang['start']['dashboard'] = '%s - dashboard';
$lang['start']['start_rc'] = 'Открыть Roundcube';
$lang['start']['start_sogo'] = "Открыть SOGo";
$lang['start']['mailcow_apps_detail'] = "Приложения для доступа к электронной почте, календарю, контактам и т.д.";
$lang['start']['mailcow_panel'] = 'Start mailcow UI';
$lang['start']['mailcow_panel_description'] = "Пользовательский интерфейс mailcow доступен для администраторов и пользователей почтовых ящиков.";
$lang['start']['mailcow_panel_detail'] = "<b>Администраторы</b> могут создавать, изменять или удалять почтовые ящики, псевдонимы и другие административные настройки.<br />
<b>Пользователи</b> могут изменять пароль, создавать временные псевдонимы (спам псевдонимы), фильтры спама и другие доступные настройки своего профиля.";
$lang['start']['recommended_config'] = "Рекомендуемые настройки (без ActiveSync)";
$lang['start']['imap_smtp_server'] = 'IMAP- and SMTP server data';
$lang['start']['imap_smtp_server_description'] = 'For the best experience we recommend to use <a href="%s" target="_blank"><b>Mozilla Thunderbird</b></a>.';
$lang['start']['imap_smtp_server_badge'] = 'Read/Write emails';
$lang['start']['imap_smtp_server_auth_info'] = 'Please use your full email address and the PLAIN authentication mechanism.<br />
Your login data will be encrypted by the server-side mandatory encryption.';
$lang['start']['managesieve'] = 'ManageSieve';
$lang['start']['managesieve_badge'] = "Фильтр почты";
$lang['start']['managesieve_description'] = 'Please use <b>Mozilla Thunderbird</b> with the <a style="text-decoration:none" target="_blank" href="%s"><b>nightly sieve extension</b></a>.<br />Start Thunderbird, open the add-on settings and drop the newly downloaded xpi file into the opened window.<br />The server name is <b>%s</b>, use port <b>4190</b> if you are asked for. The login data match your email login.';
$lang['start']['service'] = "Сервисы";
$lang['start']['encryption'] = "Метод шифрования";
$lang['start']['help'] = "Справка";
$lang['start']['hostname'] = "Имя хоста";
$lang['start']['port'] = "Порт";
$lang['start']['footer'] = '';
$lang['header']['mailcow_settings'] = "Меню";
$lang['header']['administration'] = "Административные настройки";
$lang['header']['mailboxes'] = "Управление почтовым сервером";
$lang['header']['user_settings'] = "Настройки пользователя";
$lang['header']['login'] = "Логин";
$lang['header']['logged_in_as_logout'] = "Вы вошли как <b>%s</b> (выйти)";
$lang['header']['logged_in_as_logout_dual'] = 'Вы вошли как <b>%s <span class="text-info">[%s]</span></b>';
$lang['header']['locale'] = "Язык";
$lang['mailbox']['domain'] = "Домен";
$lang['mailbox']['spam_aliases'] = "Временный псевдоним";
$lang['mailbox']['multiple_bookings'] = 'Multiple bookings';
$lang['mailbox']['kind'] = "Вид";
$lang['mailbox']['description'] = "Описание";
$lang['mailbox']['alias'] = "Псевдоним";
$lang['mailbox']['resource_name'] = 'Resource name';
$lang['mailbox']['aliases'] = "Псевдонимы";
$lang['mailbox']['domains'] = "Домены";
$lang['mailbox']['mailboxes'] = "Почтовые ящики";
$lang['mailbox']['resources'] = "Ресурсы";
$lang['mailbox']['mailbox_quota'] = "Макс. квота почтового ящика";
$lang['mailbox']['domain_quota'] = "Квота";
$lang['mailbox']['active'] = "Статус";
$lang['mailbox']['action'] = "Действия";
$lang['mailbox']['ratelimit'] = 'Outgoing rate limit/h';
$lang['mailbox']['backup_mx'] = "Резервный MX";
$lang['mailbox']['domain_aliases'] = "Псевдонимы домена";
$lang['mailbox']['target_domain'] = 'Target domain';
$lang['mailbox']['target_address'] = "Goto address";
$lang['mailbox']['username'] = "Почтовый ящик";
$lang['mailbox']['fname'] = "Имя";
$lang['mailbox']['filter_table'] = "Поиск";
$lang['mailbox']['yes'] = '&#10004;';
$lang['mailbox']['no'] = '&#10008;';
$lang['mailbox']['quota'] = "Квота";
$lang['mailbox']['in_use'] = "Занято";
$lang['mailbox']['msg_num'] = "Письма";
$lang['mailbox']['remove'] = "Удалить";
$lang['mailbox']['edit'] = "Изменить";
$lang['mailbox']['archive'] = "Архив";
$lang['mailbox']['no_record'] = 'No record for object %s';
$lang['mailbox']['no_record_single'] = "Нет записи";
$lang['mailbox']['add_domain'] = "Добавить домен";
$lang['mailbox']['add_domain_alias'] = "Добавить псевдоним домена";
$lang['mailbox']['add_mailbox'] = "Добавить почтовый ящик";
$lang['mailbox']['add_resource'] = "Добавить ресурс";
$lang['mailbox']['add_alias'] = "Добавить псевдоним";
$lang['mailbox']['add_domain_record_first'] = "Сначала добавьте домен";
$lang['info']['no_action'] = "Действий не предусмотрено";
$lang['delete']['title'] = "Удалить объект";
$lang['delete']['remove_domain_warning'] = "<b>Внимание:</b> Вы собираетесь удалить домен <b>%s</b>!";
$lang['delete']['remove_syncjob_warning'] = "<b>Внимание:</b> Вы собираетесь удалить задание синхронизации для пользователя <b>%s</b>!";
$lang['delete']['remove_domainalias_warning'] = "<b>Внимание:</b> Вы собираетесь удалить псевдоним домена <b>%s</b>!";
$lang['delete']['remove_domainadmin_warning'] = "<b>Внимание:</b> Вы собираетесь удалить администратора домена <b>%s</b>!";
$lang['delete']['remove_alias_warning'] = "<b>Внимание:</b> Вы собираетесь удалить псевдоним адрес <b>%s</b>!";
$lang['delete']['remove_mailbox_warning'] = "<b>Внимание:</b> Вы собираетесь удалить почтовый ящик <b>%s</b>!";
$lang['delete']['remove_mailbox_details'] = "Почтовый ящик будет <b>очищен навсегда</b>!";
$lang['delete']['remove_resource_warning'] = "<b>Внимание:</b> Вы собираетесь удалить ресурс <b>%s</b>!";
$lang['delete']['remove_resource_details'] = "Ресурс будет <b>очищен навсегда</b>!";
$lang['delete']['remove_domain_details'] = "Это также удаляет псевдонимы доменов.<br /><br /><b>Домен должен быть пустым для удаления.</b>";
$lang['delete']['remove_syncjob_details'] = "Объекты из этого задания синхронизации больше не будут извлекаться с удаленного сервера.";
$lang['delete']['remove_alias_details'] = "Пользователи больше не смогут получать почту или отправлять почту с этого адреса.</b>";
$lang['delete']['remove_button'] = "Удалить";
$lang['delete']['previous'] = "Предыдущая страница";
$lang['edit']['syncjob'] = "Изменить задание синхронизации";
$lang['edit']['save'] = "Сохранить";
$lang['edit']['username'] = "Логин";
$lang['edit']['hostname'] = "Имя хоста";
$lang['edit']['encryption'] = "Шифрование";
$lang['edit']['maxage'] = 'Maximum age of messages in days that will be polled from remote<br /><small>(0 = ignore age)</small>';
$lang['edit']['subfolder2'] = 'Sync into subfolder on destination<br /><small>(empty = do not use subfolder)</small>';
$lang['edit']['mins_interval'] = "Интервал (минуты)";
$lang['edit']['exclude'] = 'Exclude objects (regex)';
$lang['edit']['save'] = "Сохранить";
$lang['edit']['archive'] = "Доступ к архиву";
$lang['edit']['max_mailboxes'] = "Максимум почтовых ящиков";
$lang['edit']['title'] = "Добавить объект";
$lang['edit']['target_address'] = 'Goto address/es <small>(comma-separated)</small>';
$lang['edit']['active'] = "Активность";
$lang['edit']['target_domain'] = "Основной домен";
$lang['edit']['password'] = "Пароль";
$lang['edit']['ratelimit'] = 'Outgoing rate limit/h';
$lang['danger']['ratelimt_less_one'] = 'Outgoing rate limit/h must not be less than 1';
$lang['edit']['password_repeat'] = "Повторить пароль";
$lang['edit']['domain_admin'] = "Редактирование администратора домена";
$lang['edit']['domain'] = "Изменить домен";
$lang['edit']['alias_domain'] = "Псевдоним домена";
$lang['edit']['edit_alias_domain'] = "Изменить псевдоним домена";
$lang['edit']['domains'] = "Домены";
$lang['edit']['destroy'] = "Ручной ввод данных";
$lang['edit']['alias'] = "Изменить псевдоним";
$lang['edit']['mailbox'] = "Редактирование почтового ящика";
$lang['edit']['description'] = "Описание";
$lang['edit']['max_aliases'] = "Максимум псевдонимов";
$lang['edit']['max_quota'] = "Максимальная квота на почтовый ящик (MiB)";
$lang['edit']['domain_quota'] = "Квота домена";
$lang['edit']['backup_mx_options'] = "Настройки резервного MX";
$lang['edit']['relay_domain'] = 'Relay domain';
$lang['edit']['relay_all'] = 'Relay all recipients';
$lang['edit']['dkim_signature'] = "DKIM подпись";
$lang['edit']['dkim_record_info'] = '<small>Please add a TXT record with the given value to your DNS settings.</small>';
$lang['edit']['relay_all_info'] = '<small>If you choose <b>not</b> to relay all recipients, you will need to add a ("blind") mailbox for every single recipient that should be relayed.</small>';
$lang['edit']['full_name'] = "Имя";
$lang['edit']['quota_mb'] = "Квота (MiB)";
$lang['edit']['sender_acl'] = "Отправлять письма от <small>(выбрать какой адрес(а) можно использовать в качестве отправителя)</small>";
$lang['edit']['sender_acl_info'] = "Псевдонимы не могут быть отменены";
$lang['edit']['dkim_txt_name'] = "Имя TXT записи";
$lang['edit']['dkim_txt_value'] = "Значение TXT записи";
$lang['edit']['previous'] = "Предыдущая страница";
$lang['edit']['unchanged_if_empty'] = "Если без изменений, оставьте поле пустым";
$lang['edit']['dont_check_sender_acl'] = "Disable sender check for domain %s + alias domains";
$lang['edit']['multiple_bookings'] = 'Multiple bookings';
$lang['edit']['kind'] = "Вид";
$lang['edit']['resource'] = "Ресурс";
$lang['add']['syncjob'] = "Добавить задание синхронизации";
$lang['add']['syncjob_hint'] = "Помните, что пароли должны быть сохранены как обычный текст!";
$lang['add']['hostname'] = "Имя хоста";
$lang['add']['port'] = "Порт";
$lang['add']['username'] = "Логин";
$lang['add']['enc_method'] = "Метод шифрования";
$lang['add']['mins_interval'] = "Интервал опроса (в минутах)";
$lang['add']['maxage'] = 'Maximum age of messages that will be polled from remote (0 = ignore age)';
$lang['add']['subfolder2'] = "Синхронизировать в подпапку по назначению";
$lang['add']['exclude'] = "Исключить объекты (regex)";
$lang['add']['delete2duplicates'] = "Удалить дубликаты в получателях";
$lang['add']['title'] = "Добавить объект";
$lang['add']['domain'] = "Домен";
$lang['add']['active'] = "Активный";
$lang['add']['multiple_bookings'] = 'Multiple bookings';
$lang['add']['save'] = "Сохранить изменения";
$lang['add']['description'] = "Описание";
$lang['add']['max_aliases'] = "Максимум псевдонимов";
$lang['add']['resource_name'] = "Имя ресурса";
$lang['add']['max_mailboxes'] = "Максимум почтовых ящиков";
$lang['add']['mailbox_quota_m'] = "Максимальная квота на почтовый ящик (MiB)";
$lang['add']['domain_quota_m'] = "Общая квота домена (MiB)";
$lang['add']['backup_mx_options'] = "Настройки резервного MX";
$lang['add']['relay_all'] = "Relay all recipients";
$lang['add']['relay_domain'] = "Relay this domain";
$lang['add']['relay_all_info'] = '<small>If you choose <b>not</b> to relay all recipients, you will need to add a ("blind") mailbox for every single recipient that should be relayed.</small>';
$lang['add']['alias'] = "Псевдоним(ы)";
$lang['add']['alias_spf_fail'] = '<b>Заметка:</b> If your chosen destination address is an external mailbox, the <b>receiving mailserver</b> may reject your message due to an SPF failure.</a>';
$lang['add']['alias_address'] = "Псевдоним адрес(а)";
$lang['add']['alias_address_info'] = '<small>Full email address/es or @example.com, to catch all messages for a domain (comma-separated). <b>mailcow domains only</b>.</small>';
$lang['add']['alias_domain_info'] = '<small>Только допустимые доменные имена (через запятую).</small>';
$lang['add']['target_address'] = "Основной адрес";
$lang['add']['target_address_info'] = "<small>Адрес(а) электронной почты (через запятую).</small>";
$lang['add']['alias_domain'] = "Псевдоним домена";
$lang['add']['select'] = "Пожалуйста, выберите...";
$lang['add']['target_domain'] = "Основной домен";
$lang['add']['mailbox'] = "Почтовый ящик";
$lang['add']['resource'] = "Ресурс";
$lang['add']['kind'] = "Вид";
$lang['add']['mailbox_username'] = "Адрес электронной почты, без @example.com";
$lang['add']['full_name'] = "Имя";
$lang['add']['quota_mb'] = "Квота (MiB):";
$lang['add']['select_domain'] = "Выберите основной домен";
$lang['add']['password'] = "Пароль";
$lang['add']['password_repeat'] = "Повторить пароль";
$lang['add']['previous'] = "Предыдущая страница";
$lang['add']['restart_sogo_hint'] = "После добавления нового домена потребуется перезагрузить SOGo!";
$lang['login']['title'] = "Логин";
$lang['login']['administration'] = "Администрирование";
$lang['login']['administration_details'] = "Пожалуйста, используйте вашу учетную запись администратора для выполнения административных задач";
$lang['login']['user_settings'] = "Настройки пользователя";
$lang['login']['user_settings_details'] = "Пользователи почтовых ящиков могут использовать пользовательский интерфейс mailcow, чтобы изменять свой пароль, создавать временные псевдонимы (псевдонимы спама), настраивать поведение спам-фильтра или импортировать сообщения с удаленного сервера IMAP.";
$lang['login']['username'] = "Логин";
$lang['login']['password'] = "Пароль";
$lang['login']['reset_password'] = "Сбросить пароль";
$lang['login']['login'] = "Войти";
$lang['login']['previous'] = "Предыдущая страница";
$lang['login']['delayed'] = 'Login was delayed by %s seconds.';
$lang['tfa']['tfa'] = "Двухфакторная проверка подлинности";
$lang['tfa']['set_tfa'] = "Использовать двухфакторный метод проверки подлинности";
$lang['tfa']['yubi_otp'] = "Yubico OTP аутентификация";
$lang['tfa']['key_id'] = "Идентификатор для вашего YubiKey";
$lang['tfa']['api_register'] = 'mailcow использует Yubico Cloud API. Пожалуйста, получите ключ API для вашего ключа <a href="https://upgrade.yubico.com/getapikey/" target="_blank">тут</a>.';
$lang['tfa']['u2f'] = "U2F проверка подлинности";
$lang['tfa']['hotp'] = "HOTP проверка подлинности";
$lang['tfa']['totp'] = "TOTP проверка подлинности";
$lang['tfa']['none'] = "Отключен";
$lang['tfa']['delete_tfa'] = "Отключить TFA";
$lang['tfa']['disable_tfa'] = "Отключить TFA до следующего удачного входа";
$lang['tfa']['confirm_tfa'] = "Пожалуйста, подтвердите свой одноразовый пароль в поле ниже";
$lang['tfa']['confirm'] = "Подтвердить";
$lang['tfa']['otp'] = "Одноразовый пароль";
$lang['tfa']['trash_login'] = "Некорректный логин";
$lang['tfa']['select'] = "Пожалуйста выберите";
$lang['tfa']['waiting_usb_auth'] = "<i>Ждем USB-устройства...</i><br /><br />Пожалуйста, нажмите кнопку на USB-устройстве U2F.";
$lang['tfa']['waiting_usb_register'] = "<i>Ждем USB-устройства...</i><br /><br />Пожалуйста, введите свой пароль выше и подтвердите регистрацию U2F, нажав кнопку на USB-устройстве U2F.";
$lang['admin']['search_domain_da'] = "Поиск домена";
$lang['admin']['restrictions'] = "Настройки Postfix";
$lang['admin']['rr'] = "Настройки получателя";
$lang['admin']['sr'] = "Настройки отправителя";
$lang['admin']['reset_defaults'] = "Сброс по умолчанию";
$lang['admin']['sr'] = "Настройки отправителя";
$lang['admin']['r_inactive'] = "Неактивные элементы";
$lang['admin']['r_active'] = "Активные элементы";
$lang['admin']['r_info'] = "Выделенные серым цветом элементы в списке активных элементов заблокированы от перемещения в список неактивных элементов, так как их отключение может непредсказуемо повлиять на работу mailcow. Unknown restrictions will be set in order of appearance anyway. <br />Вы можете добавить новые элементы в <code>inc/vars.local.inc.php</code>, что бы иметь возможно переключать их.";
$lang['admin']['public_folders'] = "Общие папки";
$lang['admin']['public_folders_text'] = 'A namespace "Public" is created. Below\'s public folder name indicates the name of the first auto-created mailbox within this namespace.';
$lang['admin']['public_folder_name'] = "Имя папки";
$lang['admin']['public_folder_enable'] = "Включение общих папок";
$lang['admin']['public_folder_enable_text'] = "Отключение этой опции не приведет к удалению почты из общих папок";
$lang['admin']['public_folder_pusf'] = 'Enable per-user seen flag';
$lang['admin']['public_folder_pusf_text'] = 'A "per-user seen flag"-enabled system will not mark a mail as read for User B, when User A has seen it, but User B did not.';
$lang['admin']['privacy'] = "Конфедоциальность";
$lang['admin']['privacy_text'] = 'This option enables a PCRE table to remove "User-Agent", "X-Enigmail", "X-Mailer", "X-Originating-IP" and replaces "Received: from" headers with localhost/127.0.0.1.';
$lang['admin']['privacy_anon_mail'] = "Анонимизировать исходящую почту";
$lang['admin']['dkim_txt_name'] = "Имя TXT записи";
$lang['admin']['dkim_txt_value'] = "Значение TXT записи";
$lang['admin']['dkim_key_length'] = "Длина ключа";
$lang['admin']['dkim_key_valid'] = "Ключ действителен";
$lang['admin']['dkim_key_unused'] = "Неиспользованный ключ";
$lang['admin']['dkim_key_missing'] = "Ключ отсутствует";
$lang['admin']['dkim_key_hint'] = "Селектор для DKIM ключей <code>dkim</code>. Подсказка: При добавлении записи на DNS-сервере, укажите Имя TXT записи <code>dkim._domainkey</code>.";
$lang['admin']['previous'] = "Предыдущая страница";
$lang['admin']['quota_mb'] = "Квота (MiB)";
$lang['admin']['sender_acl'] = "Разрешить отправлять письма от";
$lang['admin']['msg_size'] = "Максимальный размер письма";
$lang['admin']['msg_size_limit'] = "Сейчас максимальный размер письма";
$lang['admin']['msg_size_limit_details'] = "Применение нового ограничения перезагрузит Postfix и веб-сервер";
$lang['admin']['save'] = "Сохранить изменения";
$lang['admin']['maintenance'] = "Техническое обслуживание и информация";
$lang['admin']['sys_info'] = "Системная информация";
$lang['admin']['dkim_add_key'] = "Добавить DKIM ключ";
$lang['admin']['dkim_keys'] = "DKIM ключи";
$lang['admin']['add'] = "Добавить";
$lang['admin']['configuration'] = "Конфигурации";
$lang['admin']['password'] = "Пароль";
$lang['admin']['password_repeat'] = "Повторить пароль";
$lang['admin']['active'] = "Активный";
$lang['admin']['action'] = "Действия";
$lang['admin']['add_domain_admin'] = "Добавить администратора домена";
$lang['admin']['admin_domains'] = "Домен";
$lang['admin']['domain_admins'] = "Администраторы домена";
$lang['admin']['username'] = "Логин";
$lang['admin']['edit'] = "Изменить";
$lang['admin']['remove'] = "Удалить";
$lang['admin']['save'] = "Сохранить изменения";
$lang['admin']['admin'] = "Администратор";
$lang['admin']['admin_details'] = "Изменить данные администратора";
$lang['admin']['unchanged_if_empty'] = "Если без изменений, оставьте поле пустым";
$lang['admin']['yes'] = '&#10004;';
$lang['admin']['no'] = '&#10008;';
$lang['admin']['access'] = "Права доступа";
$lang['admin']['invalid_max_msg_size'] = "Неверно указан максимальный размер письма";
$lang['admin']['site_not_found'] = "Не удается найти конфигурацию mailcow";
$lang['admin']['public_folder_empty'] = "Имя общей папки не может быть пустым";
$lang['admin']['set_rr_failed'] = "Не установить настройки Postfix";
$lang['admin']['no_record'] = "Нет записей";
?>

View File

@ -5,16 +5,34 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
require_once "inc/header.inc.php"; require_once "inc/header.inc.php";
$_SESSION['return_to'] = $_SERVER['REQUEST_URI']; $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
?> ?>
<style>
table.footable>tbody>tr.footable-empty>td {
font-size:15px !important;
font-style:italic;
}
.pagination a {
text-decoration: none !important;
}
.panel panel-default {
overflow: visible !important;
}
.table-responsive {
overflow: visible !important;
}
.footer-add-item {
text-align:center;
font-style: italic;
display:block;
padding: 10px;
}
</style>
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"><?=$lang['mailbox']['domains'];?> <span class="badge" id="numRowsDomain"></span></h3> <h3 class="panel-title"><?=$lang['mailbox']['domains'];?></h3>
<div class="pull-right"> <div class="pull-right">
<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
<i class="glyphicon glyphicon-filter"></i>
</span>
<?php <?php
if ($_SESSION['mailcow_cc_role'] == "admin"): if ($_SESSION['mailcow_cc_role'] == "admin"):
?> ?>
@ -24,93 +42,10 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
?> ?>
</div> </div>
</div> </div>
<div class="panel-body"> <div class="table-responsive">
<input type="text" class="form-control" id="domaintable-filter" data-action="filter" data-filters="#domaintable" placeholder="Filter" /> <table id="domain_table" class="table table-striped"></table>
</div> </div>
<div class="table-responsive"> <span class="footer-add-item"><a href="/add.php?domain"><?=$lang['mailbox']['add_domain'];?></a></span>
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domaintable">
<thead>
<tr>
<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
<th class="sort-table" style="min-width: 81px;"><?=$lang['mailbox']['aliases'];?></th>
<th class="sort-table" style="min-width: 99px;"><?=$lang['mailbox']['mailboxes'];?></th>
<th class="sort-table" style="min-width: 172px;"><?=$lang['mailbox']['mailbox_quota'];?></th>
<th class="sort-table" style="min-width: 117px;"><?=$lang['mailbox']['domain_quota'];?></th>
<?php
if ($_SESSION['mailcow_cc_role'] == "admin"):
?>
<th class="sort-table" style="min-width: 105px;"><?=$lang['mailbox']['backup_mx'];?></th>
<?php
endif;
?>
<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
</tr>
</thead>
<tbody>
<?php
$domains = mailbox_get_domains();
if (!empty($domains)):
foreach ($domains as $domain):
$domaindata = mailbox_get_domain_details($domain);
?>
<tr id="data">
<td><?=htmlspecialchars($domaindata['domain_name']);?></td>
<td><?=$domaindata['aliases_in_domain'];?> / <?=$domaindata['max_num_aliases_for_domain'];?></td>
<td><?=$domaindata['mboxes_in_domain'];?> / <?=$domaindata['max_num_mboxes_for_domain'];?></td>
<td><?=formatBytes($domaindata['max_quota_for_mbox']);?></td>
<td><?=formatBytes($domaindata['quota_used_in_domain'], 2);?> / <?=formatBytes($domaindata['max_quota_for_domain'], 2);?></td>
<?php
if ($_SESSION['mailcow_cc_role'] == "admin"):
?>
<td><?=$domaindata['backupmx'];?></td>
<?php
endif;
?>
<td><?=$domaindata['active'];?></td>
<?php
if ($_SESSION['mailcow_cc_role'] == "admin"):
?>
<td style="text-align: right;">
<div class="btn-group">
<a href="/edit.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
<a href="/delete.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
</div>
</td>
<?php
else:
?>
<td style="text-align: right;">
<div class="btn-group">
<a href="/edit.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
</div>
</td>
</tr>
<?php
endif;
endforeach;
else:
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record_single'];?></td></tr>
<?php
endif;
?>
</tbody>
<?php
if ($_SESSION['mailcow_cc_role'] == "admin"):
?>
<tfoot>
<tr id="no-data">
<td colspan="999" style="text-align: center; font-style: normal; border-top: 1px solid #e7e7e7;">
<a href="/add.php?domain"><?=$lang['mailbox']['add_domain'];?></a>
</td>
</tr>
</tfoot>
<?php
endif;
?>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -118,91 +53,15 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"><?=$lang['mailbox']['mailboxes'];?> <span class="badge" id="numRowsMailbox"></span></h3> <h3 class="panel-title"><?=$lang['mailbox']['mailboxes'];?></h3>
<div class="pull-right"> <div class="pull-right">
<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
<i class="glyphicon glyphicon-filter"></i>
</span>
<a href="/add.php?mailbox"><span class="glyphicon glyphicon-plus"></span></a> <a href="/add.php?mailbox"><span class="glyphicon glyphicon-plus"></span></a>
</div> </div>
</div> </div>
<div class="panel-body"> <div class="table-responsive">
<input type="text" class="form-control" id="mailboxtable-filter" data-action="filter" data-filters="#mailboxtable" placeholder="Filter" /> <table id="mailbox_table" class="table table-striped"></table>
</div> </div>
<div class="table-responsive"> <span class="footer-add-item"><a href="/add.php?mailbox"><?=$lang['mailbox']['add_mailbox'];?></a></span>
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="mailboxtable">
<thead>
<tr>
<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['username'];?></th>
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['fname'];?></th>
<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['quota'];?></th>
<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['spam_aliases'];?></th>
<th class="sort-table" style="min-width: 99px;"><?=$lang['mailbox']['in_use'];?></th>
<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['msg_num'];?></th>
<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
</tr>
</thead>
<tbody>
<?php
if (!empty($domains)) {
foreach (mailbox_get_domains() as $domain) {
$mailboxes = mailbox_get_mailboxes($domain);
if (!empty($mailboxes)) {
foreach ($mailboxes as $mailbox) {
$mailboxdata = mailbox_get_mailbox_details($mailbox);
?>
<tr id="data">
<td><?=($mailboxdata['is_relayed'] == "0") ? htmlspecialchars($mailboxdata['username']) : '<span data-toggle="tooltip" title="Relayed"><i class="glyphicon glyphicon-forward"></i>' . htmlspecialchars($mailboxdata['username']) . '</span>';?></td>
<td><?=htmlspecialchars($mailboxdata['name'], ENT_QUOTES, 'UTF-8');?></td>
<td><?=htmlspecialchars($mailboxdata['domain']);?></td>
<td><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?></td>
<td><?=$mailboxdata['spam_aliases'];?></td>
<td style="min-width:120px;">
<div class="progress">
<div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
<?=$mailboxdata['percent_in_use'];?>%
</div>
</div>
</td>
<td><?=$mailboxdata['messages'];?></td>
<td><?=$mailboxdata['active'];?></td>
<td style="text-align: right;">
<div class="btn-group">
<a href="/edit.php?mailbox=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
<a href="/delete.php?mailbox=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
<?php if ($_SESSION['mailcow_cc_role'] == "admin"): ?>
<a href="/index.php?duallogin=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-success"><span class="glyphicon glyphicon-user"></span> Login</a>
<?php endif; ?>
</div>
</td>
</tr>
<?php
}
}
else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<?php
}
}
} else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
<?php
}
?>
</tbody>
<tfoot>
<tr id="no-data">
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
<a href="/add.php?mailbox"><?=$lang['mailbox']['add_mailbox'];?></a>
</td>
</tr>
</tfoot>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -210,150 +69,30 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"><?=$lang['mailbox']['resources'];?> <span class="badge" id="numRowsResource"></span></h3> <h3 class="panel-title"><?=$lang['mailbox']['resources'];?></h3>
<div class="pull-right"> <div class="pull-right">
<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
<i class="glyphicon glyphicon-filter"></i>
</span>
<a href="/add.php?resource"><span class="glyphicon glyphicon-plus"></span></a> <a href="/add.php?resource"><span class="glyphicon glyphicon-plus"></span></a>
</div> </div>
</div> </div>
<div class="panel-body"> <div class="table-responsive">
<input type="text" class="form-control" id="resourcetable-filter" data-action="filter" data-filters="#resourcetable" placeholder="Filter" /> <table id="resources_table" class="table table-striped"></table>
</div> </div>
<div class="table-responsive"> <span class="footer-add-item"><a href="/add.php?resource"><?=$lang['mailbox']['add_resource'];?></a></span> </div>
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="resourcetable">
<thead>
<tr>
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['description'];?></th>
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['kind'];?></th>
<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['multiple_bookings'];?></th>
<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
</tr>
</thead>
<tbody>
<?php
if (!empty($domains)) {
foreach (mailbox_get_domains() as $domain) {
$resources = mailbox_get_resources($domain);
if (!empty($resources)) {
foreach ($resources as $resource) {
$resourcedata = mailbox_get_resource_details($resource);
?>
<tr id="data">
<td><?=htmlspecialchars($resourcedata['description'], ENT_QUOTES, 'UTF-8');?></td>
<td><?=$resourcedata['kind'];?></td>
<td><?=htmlspecialchars($resourcedata['domain']);?></td>
<td><?=$resourcedata['multiple_bookings'];?></td>
<td><?=$resourcedata['active'];?></td>
<td style="text-align: right;">
<div class="btn-group">
<a href="/edit.php?resource=<?=urlencode($resourcedata['name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
<a href="/delete.php?resource=<?=urlencode($resourcedata['name']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
</div>
</td>
</tr>
<?php
}
}
else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<?php
}
}
} else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
<?php
}
?>
</tbody>
<tfoot>
<tr id="no-data">
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
<a href="/add.php?resource"><?=$lang['mailbox']['add_resource'];?></a>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"><?=$lang['mailbox']['domain_aliases'];?> <span class="badge" id="numRowsDomainAlias"></span></h3> <h3 class="panel-title"><?=$lang['mailbox']['domain_aliases'];?></h3>
<div class="pull-right"> <div class="pull-right">
<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
<i class="glyphicon glyphicon-filter"></i>
</span>
<a href="/add.php?aliasdomain"><span class="glyphicon glyphicon-plus"></span></a> <a href="/add.php?aliasdomain"><span class="glyphicon glyphicon-plus"></span></a>
</div> </div>
</div> </div>
<div class="panel-body"> <div class="table-responsive">
<input type="text" class="form-control" id="domainaliastable-filter" data-action="filter" data-filters="#domainaliastable" placeholder="Filter" /> <table id="aliasdomain_table" class="table table-striped"></table>
</div> </div>
<div class="table-responsive"> <span class="footer-add-item"><a href="/add.php?aliasdomain"><?=$lang['mailbox']['add_domain_alias'];?></a></span> </div>
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domainaliastable">
<thead>
<tr>
<th class="sort-table" style="min-width: 67px;"><?=$lang['mailbox']['alias'];?></th>
<th class="sort-table" style="min-width: 127px;"><?=$lang['mailbox']['target_domain'];?></th>
<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
</tr>
</thead>
<tbody>
<?php
if (!empty($domains)) {
foreach (mailbox_get_domains() as $domain) {
$alias_domains = mailbox_get_alias_domains($domain);
if (!empty($alias_domains)) {
foreach ($alias_domains as $alias_domain) {
$aliasdomaindata = mailbox_get_alias_domain_details($alias_domain);
?>
<tr id="data">
<td><?=htmlspecialchars($aliasdomaindata['alias_domain']);?></td>
<td><?=htmlspecialchars($aliasdomaindata['target_domain']);?></td>
<td><?=$aliasdomaindata['active'];?></td>
<td style="text-align: right;">
<div class="btn-group">
<a href="/edit.php?aliasdomain=<?=urlencode($aliasdomaindata['alias_domain']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
<a href="/delete.php?aliasdomain=<?=urlencode($aliasdomaindata['alias_domain']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
</div>
</td>
</tr>
<?php
}
}
else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<?php
}
}
} else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
<?php
}
?>
</tbody>
<tfoot>
<tr id="no-data">
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
<a href="/add.php?aliasdomain"><?=$lang['mailbox']['add_domain_alias'];?></a>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div> </div>
</div> </div>
@ -361,87 +100,28 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<div class="col-md-12"> <div class="col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"><?=$lang['mailbox']['aliases'];?> <span class="badge" id="numRowsAlias"></span></h3> <h3 class="panel-title"><?=$lang['mailbox']['aliases'];?></h3>
<div class="pull-right"> <div class="pull-right">
<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
<i class="glyphicon glyphicon-filter"></i>
</span>
<a href="/add.php?alias"><span class="glyphicon glyphicon-plus"></span></a> <a href="/add.php?alias"><span class="glyphicon glyphicon-plus"></span></a>
</div> </div>
</div> </div>
<div class="panel-body"> <div class="table-responsive">
<input type="text" class="form-control" id="aliastable-filter" data-action="filter" data-filters="#aliastable" placeholder="Filter" /> <table id="alias_table" class="table table-striped"></table>
</div> </div>
<div class="table-responsive"> <span class="footer-add-item"><a href="/add.php?alias"><?=$lang['mailbox']['add_alias'];?></a></span> </div>
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="aliastable">
<thead>
<tr>
<th class="sort-table" style="min-width: 67px;"><?=$lang['mailbox']['alias'];?></th>
<th class="sort-table" style="min-width: 119px;"><?=$lang['mailbox']['target_address'];?></th>
<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
</tr>
</thead>
<tbody>
<?php
if (!empty($domains)) {
foreach (array_merge(mailbox_get_domains(), mailbox_get_alias_domains()) as $domain) {
$aliases = mailbox_get_aliases($domain);
if (!empty($aliases)) {
foreach ($aliases as $alias) {
$aliasdata = mailbox_get_alias_details($alias);
?>
<tr id="data">
<td>
<?= ($aliasdata['is_catch_all'] == "1") ? '<span class="glyphicon glyphicon-pushpin" aria-hidden="true"></span> Catch-all ' . htmlspecialchars($aliasdata['address']) : htmlspecialchars($aliasdata['address']); ?>
</td>
<td>
<?php
foreach(explode(",", $aliasdata['goto']) as $goto) {
echo nl2br(htmlspecialchars($goto.PHP_EOL));
}
?>
</td>
<td><?=htmlspecialchars($aliasdata['domain']);?></td>
<td><?=$aliasdata['active'];?></td>
<td style="text-align: right;">
<div class="btn-group">
<a href="/edit.php?alias=<?=urlencode($aliasdata['address']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
<a href="/delete.php?alias=<?=urlencode($aliasdata['address']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
</div>
</td>
</tr>
<?php
}
}
else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<?php
}
}
} else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
<?php
}
?>
</tbody>
<tfoot>
<tr id="no-data">
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
<a href="/add.php?alias"><?=$lang['mailbox']['add_alias'];?></a>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div> </div>
</div> </div>
</div> <!-- /container --> </div> <!-- /container -->
<script src="js/sorttable.js"></script> <script type='text/javascript'>
<?php
$lang_mailbox = json_encode($lang['mailbox']);
echo "var lang = ". $lang_mailbox . ";\n";
$role = ($_SESSION['mailcow_cc_role'] == "admin") ? 'admin' : 'domainadmin';
echo "var role = '". $role . "';\n";
echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";
?>
</script>
<script src="js/footable.min.js"></script>
<script src="js/mailbox.js"></script> <script src="js/mailbox.js"></script>
<?php <?php
require_once("inc/footer.inc.php"); require_once("inc/footer.inc.php");

View File

@ -3,8 +3,7 @@ require_once('inc/prerequisites.inc.php');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
$scheme = isset($_SERVER['HTTPS']) ? "https://" : "http://"; $u2f = new u2flib_server\U2F('https://' . $_SERVER['SERVER_NAME']);
$u2f = new u2flib_server\U2F($scheme . $_SERVER['HTTP_HOST']);
function getRegs($username) { function getRegs($username) {
global $pdo; global $pdo;

View File

@ -17,7 +17,7 @@ This is just an example of how to obtain certificates with certbot. There are se
wget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot && chmod +x /usr/local/sbin/certbot wget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot && chmod +x /usr/local/sbin/certbot
``` ```
2\. Make sure you set `HTTP_BIND=0.0.0.0` in `mailcow.conf` or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then restart Nginx: 2\. Make sure you set `HTTP_BIND=0.0.0.0` and `HTTP_PORT=80` in `mailcow.conf` or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then restart Nginx:
``` bash ``` bash
docker-compose restart nginx-mailcow docker-compose restart nginx-mailcow
``` ```
@ -64,6 +64,8 @@ docker-compose exec rspamd-mailcow rspamadm pw
enable_password = "myhash"; enable_password = "myhash";
``` ```
You can use `password = "myhash";` instead of `enable_password` to disable write-access in the web UI.
3\. Restart rspamd: 3\. Restart rspamd:
``` ```
docker-compose restart rspamd-mailcow docker-compose restart rspamd-mailcow
@ -83,6 +85,7 @@ HTTP_PORT=8080
HTTPS_PORT=127.0.0.1 HTTPS_PORT=127.0.0.1
HTTPS_PORT=8443 HTTPS_PORT=8443
``` ```
** IMPORTANT: Do not use port 8081 **
Recreate affected containers by running `docker-compose up -d`. Recreate affected containers by running `docker-compose up -d`.

View File

@ -35,8 +35,9 @@ nano mailcow.conf
``` ```
If you plan to use a reverse proxy, you can, for example, bind HTTPS to 127.0.0.1 on port 8443 and HTTP to 127.0.0.1 on port 8080. If you plan to use a reverse proxy, you can, for example, bind HTTPS to 127.0.0.1 on port 8443 and HTTP to 127.0.0.1 on port 8080.
5\. Run the composer file. It will pull images and build containers. 5\. Pull the images and run the composer file. The paramter `-d` will start mailcow: dockerized detached:
``` ```
docker-compose pull
docker-compose up -d docker-compose up -d
``` ```
@ -48,8 +49,10 @@ The database will be initialized right after a connection to MySQL can be establ
## Update mailcow ## Update mailcow
There is no update routine. You need to refresh your pulled repository clone and apply your local changes (if any). Actually there are many ways to merge local changes. Here is one to There is no update routine. You need to refresh your pulled repository clone and apply your local changes (if any). Actually there are many ways to merge local changes.
stash all local changes, pull changes from the remote master branch and apply your stash on top of it. You will most likely see warnings about non-commited changes; you can ignore them:
### Step 1, method 1
Stash all local changes, pull changes from the remote master branch and apply your stash on top of it. You will most likely see warnings about non-commited changes; you can ignore them:
``` ```
# Stash local changes # Stash local changes
@ -60,6 +63,49 @@ git pull
git stash pop git stash pop
``` ```
### Step 1, method 2
Fetch new data from GitHub, commit changes and merge remote repository:
```
# Get updates/changes
git fetch
# Add all changed files to local clone
git add -A
# Commit changes, ignore git complaining about username and mail address
git commit -m "Local config aat $(date)"
# Merge changes
git merge
```
If git complains about conflicts, solve them! Example:
```
CONFLICT (content): Merge conflict in data/web/index.php
```
Open `data/web/index.php`, solve the conflict, close the file and run `git add -A` + `git commit -m "Solved conflict"`.
### Step 1, method 3
Thanks to fabreg @ GitHub!
In case both methods do not work (for many reason like you're unable to fix the CONFLICTS or any other reasons) you can simply start all over again.
Keep in mind that all local changes _to configuration files_ will be lost. However, your volumes will not be removed.
- Copy mailcow.conf somewhere outside the mailcow-dockerized directory
- Stop and remove mailcow containers: `docker-compose down`
- Delete the directory or rename it
- Clone the remote repository again (`git clone https://github.com/andryyy/mailcow-dockerized && cd mailcow-dockerized`). **Pay attention** to this step - the folder must have the same name of the previous one!
- Copy back your previous `mailcow.conf` into the mailcow-dockerizd folder
If you forgot to stop Docker before deleting the cloned directoy, you can use the following commands:
```
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
```
### Step 2
Pull new images (if any) and recreate changed containers: Pull new images (if any) and recreate changed containers:
``` ```
@ -67,6 +113,7 @@ docker-compose pull
docker-compose up -d --remove-orphans docker-compose up -d --remove-orphans
``` ```
### Step 3
Clean-up dangling (unused) images and volumes: Clean-up dangling (unused) images and volumes:
``` ```

View File

@ -1,3 +1,20 @@
## mailcow UI configuration
Several configuration parameters of the mailcow UI can be changed by creating a file `data/web/inc/vars.local.inc.php` which overrides defaults settings found in `data/web/inc/vars.inc.php`.
The local configuration file is persistent over updates of mailcow. Try not to change values inside `data/web/inc/vars.inc.php`, but use them as template for the local override.
mailcow UI configuration parameters can be to...
- ...change the default language*
- ...change the default bootstrap theme
- ...set a password complexity regex
- ...add mailcow app buttons to the login screen
- ...set a pagination trigger
- ...set action after submitting forms (stay in form, return to previous page)
\* To change SOGos default language, you will need to edit `data/conf/sogo/sogo.conf` and replace "English" by your preferred language.
## Anonymize headers ## Anonymize headers
Save as `data/conf/postfix/mailcow_anonymize_headers.pcre`: Save as `data/conf/postfix/mailcow_anonymize_headers.pcre`:
@ -53,24 +70,6 @@ Edit a domain as (domain) administrator to add an item to the filter table.
Beware that a mailbox user can login to mailcow and override a domain policy filter item. Beware that a mailbox user can login to mailcow and override a domain policy filter item.
## Change default language
Change `data/conf/sogo/sogo.conf` and replace "English" by your preferred language.
Create a file `data/web/inc/vars.local.inc.php` and add "DEFAULT_LANG" with either "en", "pt", "de" or "nl":
```
<?php
$DEFAULT_LANG = "de";
```
## Change UI theme
mailcow uses [Bootstrap](http://getbootstrap.com/), a HTML, CSS, and JS framework.
Open or create the file `data/web/inc/vars.local.inc.php` and change `DEFAULT_THEME` to either cerulean, cosmo, custom, cyborg, darkly, flatly, journal, paper, readable, sandstone, simplex, slate, spacelab, superhero, united or yeti (see https://bootswatch.com/):
```
<?php
$DEFAULT_THEME = "paper";
```
## Customize Dockerfiles ## Customize Dockerfiles
Make your changes in `data/Dockerfiles/$service` and build the image locally: Make your changes in `data/Dockerfiles/$service` and build the image locally:
@ -89,14 +88,14 @@ docker-compose up -d
This option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do. This option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do.
Simply create a file `data/conf/postfix/check_sender_access` and enter the following content: Simply create a file `data/conf/postfix/check_sasl_access` and enter the following content. This user must exist in your installation and needs to authenticate before sending mail.
``` ```
user-to-allow-everything@example.com OK user-to-allow-everything@example.com OK
``` ```
Open `data/conf/postfix/main.cf` and find `smtpd_sender_restrictions`. Prepend `check_sasl_access hash:/opt/postfix/conf/check_sender_access` like this: Open `data/conf/postfix/main.cf` and find `smtpd_sender_restrictions`. Prepend `check_sasl_access hash:/opt/postfix/conf/check_sasl_access` like this:
``` ```
smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...] smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sasl_access reject_authenticated_sender_login_mismatch [...]
``` ```
Run postmap on check_sasl_access: Run postmap on check_sasl_access:
@ -235,7 +234,7 @@ Open `data/conf/nginx/site.conf` and add a new "catch-all" site at the top of th
``` ```
server { server {
listen 80 default_server; listen 80 default_server;
server_name _; include /etc/nginx/conf.d/server_name.active;
return 301 https://$host$request_uri; return 301 https://$host$request_uri;
} }
``` ```
@ -286,6 +285,22 @@ The bayes statistics are written to Redis as keys `BAYES_HAM` and `BAYES_SPAM`.
You can also use Rspamd's web ui to learn ham and/or spam. You can also use Rspamd's web ui to learn ham and/or spam.
### Learn ham or spam from existing directory
You can use a one-liner to learn mail in plain-text (uncompressed) format:
```
# Ham
for file in /my/folder/cur/*; do docker exec -i $(docker-compose ps -q rspamd-mailcow) rspamc learn_ham < $file; done
# Spam
for file in /my/folder/.Junk/cur/*; do docker exec -i $(docker-compose ps -q rspamd-mailcow) rspamc learn_spam < $file; done
```
Consider attaching a local folder as new volume to `rspamd-mailcow` in `docker-compose.yml` and learn given files inside the container. This can be used as workaround to parse compressed data with zcat. Example:
```
for file in /data/old_mail/.Junk/cur/*; do rspamc learn_spam < zcat $file; done
```
### CLI tools ### CLI tools
``` ```