From 639c9615a1331ed99c410b7428fc59053bae5002 Mon Sep 17 00:00:00 2001 From: broedli Date: Wed, 8 Mar 2017 17:00:04 +0100 Subject: [PATCH 01/55] Update first_steps.md too many issues with the setup, maybe adding a dedicated pointer to `HTTP_PORT=80` will help ... --- docs/first_steps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/first_steps.md b/docs/first_steps.md index 7f2def80..5b5211f1 100644 --- a/docs/first_steps.md +++ b/docs/first_steps.md @@ -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 ``` -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 docker-compose restart nginx-mailcow ``` From 489b1ff1c06dd62588abc53221f36c036c89881f Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 8 Mar 2017 17:46:21 +0100 Subject: [PATCH 02/55] Fix domain visibility in SOGo (domains should _not_ see each other) --- data/Dockerfiles/sogo/reconf-domains.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/Dockerfiles/sogo/reconf-domains.sh b/data/Dockerfiles/sogo/reconf-domains.sh index 7ec4e173..9ea2f6dd 100755 --- a/data/Dockerfiles/sogo/reconf-domains.sh +++ b/data/Dockerfiles/sogo/reconf-domains.sh @@ -30,8 +30,6 @@ cat < /var/lib/sogo/GNUstep/Defaults/sogod.plist mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_cache_folder OCSEMailAlarmsFolderURL mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_alarms_folder - DomainFieldName - domain OCSFolderInfoURL mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_folder_info OCSSessionsFolderURL @@ -63,6 +61,8 @@ while read line KindFieldName kind + DomainFieldName + domain MultipleBookingsFieldName multiple_bookings canAuthenticate From 276e37098912691a6a7976ee4c3545597ec53f09 Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 8 Mar 2017 17:58:00 +0100 Subject: [PATCH 03/55] Rspamd tag check for non-spam only (post-filter), remove sql files from repository" --- data/Dockerfiles/dovecot/docker-entrypoint.sh | 2 + data/conf/dovecot/sql/dovecot-dict-sql.conf | 15 ---- data/conf/dovecot/sql/dovecot-mysql.conf | 6 -- .../postfix/sql/mysql_relay_recipient_maps.cf | 5 -- .../sql/mysql_tls_enforce_in_policy.cf | 5 -- .../sql/mysql_tls_enforce_out_policy.cf | 5 -- ...ysql_virtual_alias_domain_catchall_maps.cf | 6 -- .../sql/mysql_virtual_alias_domain_maps.cf | 5 -- .../postfix/sql/mysql_virtual_alias_maps.cf | 5 -- .../postfix/sql/mysql_virtual_domains_maps.cf | 5 -- .../postfix/sql/mysql_virtual_mailbox_maps.cf | 5 -- .../sql/mysql_virtual_relay_domain_maps.cf | 5 -- .../postfix/sql/mysql_virtual_sender_acl.cf | 5 -- .../sql/mysql_virtual_spamalias_maps.cf | 5 -- data/conf/rspamd/lua/rspamd.local.lua | 68 ++++++++++--------- 15 files changed, 39 insertions(+), 108 deletions(-) delete mode 100644 data/conf/dovecot/sql/dovecot-dict-sql.conf delete mode 100644 data/conf/dovecot/sql/dovecot-mysql.conf delete mode 100644 data/conf/postfix/sql/mysql_relay_recipient_maps.cf delete mode 100644 data/conf/postfix/sql/mysql_tls_enforce_in_policy.cf delete mode 100644 data/conf/postfix/sql/mysql_tls_enforce_out_policy.cf delete mode 100644 data/conf/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf delete mode 100644 data/conf/postfix/sql/mysql_virtual_alias_domain_maps.cf delete mode 100644 data/conf/postfix/sql/mysql_virtual_alias_maps.cf delete mode 100644 data/conf/postfix/sql/mysql_virtual_domains_maps.cf delete mode 100644 data/conf/postfix/sql/mysql_virtual_mailbox_maps.cf delete mode 100644 data/conf/postfix/sql/mysql_virtual_relay_domain_maps.cf delete mode 100644 data/conf/postfix/sql/mysql_virtual_sender_acl.cf delete mode 100644 data/conf/postfix/sql/mysql_virtual_spamalias_maps.cf diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index be3fff60..8ef09dba 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -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 "/^\$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 DBPASS=$(echo ${DBPASS} | sed 's/"/\\"/g') diff --git a/data/conf/dovecot/sql/dovecot-dict-sql.conf b/data/conf/dovecot/sql/dovecot-dict-sql.conf deleted file mode 100644 index 0271326e..00000000 --- a/data/conf/dovecot/sql/dovecot-dict-sql.conf +++ /dev/null @@ -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 -} - diff --git a/data/conf/dovecot/sql/dovecot-mysql.conf b/data/conf/dovecot/sql/dovecot-mysql.conf deleted file mode 100644 index edf5ca6c..00000000 --- a/data/conf/dovecot/sql/dovecot-mysql.conf +++ /dev/null @@ -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'; diff --git a/data/conf/postfix/sql/mysql_relay_recipient_maps.cf b/data/conf/postfix/sql/mysql_relay_recipient_maps.cf deleted file mode 100644 index 9eb7b159..00000000 --- a/data/conf/postfix/sql/mysql_relay_recipient_maps.cf +++ /dev/null @@ -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; diff --git a/data/conf/postfix/sql/mysql_tls_enforce_in_policy.cf b/data/conf/postfix/sql/mysql_tls_enforce_in_policy.cf deleted file mode 100644 index de40b580..00000000 --- a/data/conf/postfix/sql/mysql_tls_enforce_in_policy.cf +++ /dev/null @@ -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'; diff --git a/data/conf/postfix/sql/mysql_tls_enforce_out_policy.cf b/data/conf/postfix/sql/mysql_tls_enforce_out_policy.cf deleted file mode 100644 index 34d61331..00000000 --- a/data/conf/postfix/sql/mysql_tls_enforce_out_policy.cf +++ /dev/null @@ -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'; diff --git a/data/conf/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf b/data/conf/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf deleted file mode 100644 index 484a0eac..00000000 --- a/data/conf/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf +++ /dev/null @@ -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' - diff --git a/data/conf/postfix/sql/mysql_virtual_alias_domain_maps.cf b/data/conf/postfix/sql/mysql_virtual_alias_domain_maps.cf deleted file mode 100644 index 0ead5905..00000000 --- a/data/conf/postfix/sql/mysql_virtual_alias_domain_maps.cf +++ /dev/null @@ -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' diff --git a/data/conf/postfix/sql/mysql_virtual_alias_maps.cf b/data/conf/postfix/sql/mysql_virtual_alias_maps.cf deleted file mode 100644 index a72c8bd8..00000000 --- a/data/conf/postfix/sql/mysql_virtual_alias_maps.cf +++ /dev/null @@ -1,5 +0,0 @@ -user = mailcow -password = mysafepasswd -hosts = mysql -dbname = mailcow -query = SELECT goto FROM alias WHERE address='%s' AND active='1'; diff --git a/data/conf/postfix/sql/mysql_virtual_domains_maps.cf b/data/conf/postfix/sql/mysql_virtual_domains_maps.cf deleted file mode 100644 index 22e00938..00000000 --- a/data/conf/postfix/sql/mysql_virtual_domains_maps.cf +++ /dev/null @@ -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' diff --git a/data/conf/postfix/sql/mysql_virtual_mailbox_maps.cf b/data/conf/postfix/sql/mysql_virtual_mailbox_maps.cf deleted file mode 100644 index bf07cdb2..00000000 --- a/data/conf/postfix/sql/mysql_virtual_mailbox_maps.cf +++ /dev/null @@ -1,5 +0,0 @@ -user = mailcow -password = mysafepasswd -hosts = mysql -dbname = mailcow -query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1' diff --git a/data/conf/postfix/sql/mysql_virtual_relay_domain_maps.cf b/data/conf/postfix/sql/mysql_virtual_relay_domain_maps.cf deleted file mode 100644 index 6994d02d..00000000 --- a/data/conf/postfix/sql/mysql_virtual_relay_domain_maps.cf +++ /dev/null @@ -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' diff --git a/data/conf/postfix/sql/mysql_virtual_sender_acl.cf b/data/conf/postfix/sql/mysql_virtual_sender_acl.cf deleted file mode 100644 index daf13118..00000000 --- a/data/conf/postfix/sql/mysql_virtual_sender_acl.cf +++ /dev/null @@ -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' diff --git a/data/conf/postfix/sql/mysql_virtual_spamalias_maps.cf b/data/conf/postfix/sql/mysql_virtual_spamalias_maps.cf deleted file mode 100644 index ac8d78ac..00000000 --- a/data/conf/postfix/sql/mysql_virtual_spamalias_maps.cf +++ /dev/null @@ -1,5 +0,0 @@ -user = mailcow -password = mysafepasswd -hosts = mysql -dbname = mailcow -query = SELECT goto FROM spamalias WHERE address='%s' AND validity >= UNIX_TIMESTAMP() diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua index 133961c0..4b037f06 100644 --- a/data/conf/rspamd/lua/rspamd.local.lua +++ b/data/conf/rspamd/lua/rspamd.local.lua @@ -23,40 +23,46 @@ auth_domain_map = rspamd_config:add_map({ description = 'Map of domains we are authoritative for' }) -rspamd_config.ADD_DELIMITER_TAG = { - callback = function(task) - local tag = nil - local util = require("rspamd_util") - local rspamd_logger = require "rspamd_logger" - local user_tagged = task:get_recipients(2)[1]['user'] - local domain = task:get_recipients(1)[1]['domain'] - local user, tag = user_tagged:match("([^+]+)+(.*)") - local authdomain = auth_domain_map:get_key(domain) +rspamd_config:register_post_filter(function(task) + local tag = nil + local util = require("rspamd_util") + local rspamd_logger = require "rspamd_logger" + local user_tagged = task:get_recipients(2)[1]['user'] + local domain = task:get_recipients(1)[1]['domain'] + local user, tag = user_tagged:match("([^+]+)+(.*)") + local authdomain = auth_domain_map:get_key(domain) + local action = task:get_metric_action('default') + local action = task:get_metric_action('default') + rspamd_logger.infox("metric action now: %s", action) - if tag and authdomain then - rspamd_logger.infox("domain: %1, tag: %2", domain, tag) - local user_untagged = user .. '@' .. domain - rspamd_logger.infox("querying tag settings for user %1", user_untagged) - if modify_subject_map:get_key(user_untagged) then - rspamd_logger.infox("found user in map for subject rewrite") - 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 untagged message or authenticated user") - end + if action ~= 'no action' and action ~= 'greylist' then return false end -} + + if tag and authdomain then + rspamd_logger.infox("domain: %1, tag: %2", domain, tag) + local user_untagged = user .. '@' .. domain + rspamd_logger.infox("querying tag settings for user %1", user_untagged) + if modify_subject_map:get_key(user_untagged) then + rspamd_logger.infox("found user in map for subject rewrite") + 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 untagged message or authenticated user") + end + return false +end) + rspamd_config.MRAPTOR = { callback = function(task) From d6cdc846d8dec7c8e873fc66bb792f87d7a5711a Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 8 Mar 2017 18:17:42 +0100 Subject: [PATCH 04/55] Fix alias regex --- data/web/inc/functions.inc.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 39e0fe35..87bed9b9 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -1467,7 +1467,7 @@ function user_get_alias_details($username) { try { $data['address'] = $username; $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '✘') AS `aliases` FROM `alias` - WHERE `goto` LIKE :username_goto + WHERE `goto` REGEXP :username_goto AND `address` NOT LIKE '@%' AND `address` != :username_address"); $stmt->execute(array(':username_goto' => '(^|,)'.$username.'($|,)', ':username_address' => $username)); @@ -1495,8 +1495,8 @@ function user_get_alias_details($username) { while ($row = array_shift($run)) { $data['aliases_send_as_all'] = $row['send_as']; } - $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '✘') as `address` FROM `alias` WHERE `goto` LIKE :username AND `address` LIKE '@%';"); - $stmt->execute(array(':username' => '%' . $username . '%')); + $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '✘') as `address` FROM `alias` WHERE `goto` REGEXP :username AND `address` LIKE '@%';"); + $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)')); $run = $stmt->fetchAll(PDO::FETCH_ASSOC); while ($row = array_shift($run)) { $data['is_catch_all'] = $row['address']; @@ -4878,7 +4878,7 @@ function mailbox_get_sender_acl_handles($mailbox) { try { // 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.'($|,)')); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); while ($row = array_shift($rows)) { From e2dc15774fd1e9eadd1c119a486e26b9af95c3de Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 9 Mar 2017 10:48:04 +0100 Subject: [PATCH 05/55] Fix u2f with nginx rp --- data/web/inc/prerequisites.inc.php | 3 +-- data/web/u2f_api.php | 5 ++--- docs/first_steps.md | 2 ++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index ffa1eac6..b5f150fc 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -26,8 +26,7 @@ require_once 'inc/lib/Yubico.php'; // U2F API require_once 'inc/lib/U2F.php'; -$scheme = isset($_SERVER['HTTPS']) ? "https://" : "http://"; -$u2f = new u2flib_server\U2F($scheme . $_SERVER['HTTP_HOST']); +$u2f = new u2flib_server\U2F('https://' . $_SERVER['HTTP_HOST']); // PDO $dsn = "$database_type:host=$database_host;dbname=$database_name"; diff --git a/data/web/u2f_api.php b/data/web/u2f_api.php index 634757ce..cd3a4ab6 100644 --- a/data/web/u2f_api.php +++ b/data/web/u2f_api.php @@ -3,8 +3,7 @@ require_once('inc/prerequisites.inc.php'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); -$scheme = isset($_SERVER['HTTPS']) ? "https://" : "http://"; -$u2f = new u2flib_server\U2F($scheme . $_SERVER['HTTP_HOST']); +$u2f = new u2flib_server\U2F('https://' . $_SERVER['HTTP_HOST']); function getRegs($username) { global $pdo; @@ -154,4 +153,4 @@ Action:
} ?> - \ No newline at end of file + diff --git a/docs/first_steps.md b/docs/first_steps.md index 5b5211f1..91cfb581 100644 --- a/docs/first_steps.md +++ b/docs/first_steps.md @@ -83,6 +83,7 @@ HTTP_PORT=8080 HTTPS_PORT=127.0.0.1 HTTPS_PORT=8443 ``` +** IMPORTANT: Do not use port 8081 ** Recreate affected containers by running `docker-compose up -d`. @@ -125,6 +126,7 @@ server { location / { proxy_pass http://127.0.0.1:8080/; proxy_redirect http://127.0.0.1:8080/ $scheme://$host:$server_port/; + proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; From 9e2d9a5bcdaf676e0be35af14c5ab20c8253120d Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 9 Mar 2017 11:10:25 +0100 Subject: [PATCH 06/55] Fix u2f with nginx rp --- data/web/inc/prerequisites.inc.php | 2 +- data/web/u2f_api.php | 2 +- docs/first_steps.md | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index b5f150fc..5a6ed18c 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -26,7 +26,7 @@ require_once 'inc/lib/Yubico.php'; // U2F API require_once 'inc/lib/U2F.php'; -$u2f = new u2flib_server\U2F('https://' . $_SERVER['HTTP_HOST']); +$u2f = new u2flib_server\U2F('https://' . $_SERVER['SERVER_NAME']); // PDO $dsn = "$database_type:host=$database_host;dbname=$database_name"; diff --git a/data/web/u2f_api.php b/data/web/u2f_api.php index cd3a4ab6..ddeb1ece 100644 --- a/data/web/u2f_api.php +++ b/data/web/u2f_api.php @@ -3,7 +3,7 @@ require_once('inc/prerequisites.inc.php'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); -$u2f = new u2flib_server\U2F('https://' . $_SERVER['HTTP_HOST']); +$u2f = new u2flib_server\U2F('https://' . $_SERVER['SERVER_NAME']); function getRegs($username) { global $pdo; diff --git a/docs/first_steps.md b/docs/first_steps.md index 91cfb581..2202794c 100644 --- a/docs/first_steps.md +++ b/docs/first_steps.md @@ -126,7 +126,6 @@ server { location / { proxy_pass http://127.0.0.1:8080/; proxy_redirect http://127.0.0.1:8080/ $scheme://$host:$server_port/; - proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; From 4c8288f85e5164927a6e9953ce13a6182f30bfa5 Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 9 Mar 2017 17:06:58 +0100 Subject: [PATCH 07/55] Use tag settings for alias addresses --- data/conf/rspamd/dynmaps/tags.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/data/conf/rspamd/dynmaps/tags.php b/data/conf/rspamd/dynmaps/tags.php index 20210ef1..7552575c 100644 --- a/data/conf/rspamd/dynmaps/tags.php +++ b/data/conf/rspamd/dynmaps/tags.php @@ -12,16 +12,23 @@ $opt = [ try { $pdo = new PDO($dsn, $database_user, $database_pass, $opt); $stmt = $pdo->query("SELECT `username` FROM `mailbox` WHERE `wants_tagged_subject` = '1'"); - $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); - while ($row = array_shift($rows)) { + $rows_a = $stmt->fetchAll(PDO::FETCH_ASSOC); + 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; - 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';"); - $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); - while ($row = array_shift($rows)) { + $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';"); + $rows_b = $stmt->fetchAll(PDO::FETCH_ASSOC); + while ($row_b = array_shift($rows_b)) { $has_object = 1; - echo strtolower(trim($row['tag_ad'])) . PHP_EOL; + echo strtolower(trim($row_b['tag_ad'])) . PHP_EOL; } if ($has_object == 0) { echo "dummy@domain.local"; From 7a2427bf9ba42f7e867ab57a0e2d9278c896500b Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 10 Mar 2017 15:34:23 +0100 Subject: [PATCH 08/55] Add Junk-E-Mail as special use junk --- data/conf/dovecot/dovecot.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/conf/dovecot/dovecot.conf b/data/conf/dovecot/dovecot.conf index 0565b99e..1d05571e 100644 --- a/data/conf/dovecot/dovecot.conf +++ b/data/conf/dovecot/dovecot.conf @@ -131,6 +131,9 @@ namespace inbox { auto = subscribe special_use = \Junk } + mailbox "Junk-E-mail" { + special_use = \Junk + } mailbox "Junk E-mail" { special_use = \Junk } From 09850a76e5061c1d760eae403c07a5fd944eba93 Mon Sep 17 00:00:00 2001 From: andryyy Date: Sun, 12 Mar 2017 14:38:20 +0100 Subject: [PATCH 09/55] Use Rspamds new tag symbol --- data/Dockerfiles/rspamd/Dockerfile | 4 +- data/conf/rspamd/lua/rspamd.local.lua | 71 ++++++++++++++------------- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/data/Dockerfiles/rspamd/Dockerfile b/data/Dockerfiles/rspamd/Dockerfile index 70a0bbab..8d0a7f0a 100644 --- a/data/Dockerfiles/rspamd/Dockerfile +++ b/data/Dockerfiles/rspamd/Dockerfile @@ -9,8 +9,8 @@ RUN dpkg-divert --local --rename --add /sbin/initctl \ && dpkg-divert --local --rename --add /usr/bin/ischroot \ && ln -sf /bin/true /usr/bin/ischroot -RUN apt-key adv --fetch-keys http://rspamd.com/apt-stable/gpg.key \ - && echo "deb http://rspamd.com/apt-stable/ xenial main" > /etc/apt/sources.list.d/rspamd.list \ +RUN apt-key adv --fetch-keys http://rspamd.com/apt/gpg.key \ + && echo "deb http://rspamd.com/apt/ xenial main" > /etc/apt/sources.list.d/rspamd.list \ && apt-get update \ && apt-get -y install rspamd ca-certificates python-pip diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua index 4b037f06..6f738f92 100644 --- a/data/conf/rspamd/lua/rspamd.local.lua +++ b/data/conf/rspamd/lua/rspamd.local.lua @@ -24,46 +24,51 @@ auth_domain_map = rspamd_config:add_map({ }) rspamd_config:register_post_filter(function(task) - local tag = nil local util = require("rspamd_util") local rspamd_logger = require "rspamd_logger" - local user_tagged = task:get_recipients(2)[1]['user'] - local domain = task:get_recipients(1)[1]['domain'] - local user, tag = user_tagged:match("([^+]+)+(.*)") - local authdomain = auth_domain_map:get_key(domain) - local action = task:get_metric_action('default') - local action = task:get_metric_action('default') - rspamd_logger.infox("metric action now: %s", action) - if action ~= 'no action' and action ~= 'greylist' then + local tagged_rcpt = task:get_symbol("TAGGED_RCPT") + 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) + + if tagged_rcpt[1].options[1] then + local tag = tagged_rcpt[1].options[1] + rspamd_logger.infox("found tag: %s", tag) + local action = task:get_metric_action('default') + rspamd_logger.infox("metric action now: %s", action) + + if action ~= 'no action' and action ~= 'greylist' then + rspamd_logger.infox("skipping tag handler for action: %s", action) + return false + end + + 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 - - if tag and authdomain then - rspamd_logger.infox("domain: %1, tag: %2", domain, tag) - local user_untagged = user .. '@' .. domain - rspamd_logger.infox("querying tag settings for user %1", user_untagged) - if modify_subject_map:get_key(user_untagged) then - rspamd_logger.infox("found user in map for subject rewrite") - 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 untagged message or authenticated user") - end - return false end) - rspamd_config.MRAPTOR = { callback = function(task) local parts = task:get_parts() From 9f4a5b183450dc6bd04eb776c4f71145b91dc505 Mon Sep 17 00:00:00 2001 From: andryyy Date: Sun, 12 Mar 2017 17:25:30 +0100 Subject: [PATCH 10/55] Fix symbol check --- data/conf/rspamd/lua/rspamd.local.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua index 6f738f92..236734f2 100644 --- a/data/conf/rspamd/lua/rspamd.local.lua +++ b/data/conf/rspamd/lua/rspamd.local.lua @@ -33,7 +33,7 @@ rspamd_config:register_post_filter(function(task) local rcpt = user .. '@' .. domain local authdomain = auth_domain_map:get_key(domain) - if tagged_rcpt[1].options[1] then + if tagged_rcpt then local tag = tagged_rcpt[1].options[1] rspamd_logger.infox("found tag: %s", tag) local action = task:get_metric_action('default') From 72a58b8c280751520001852a674f1df6e00d309e Mon Sep 17 00:00:00 2001 From: andryyy Date: Mon, 13 Mar 2017 08:02:45 +0100 Subject: [PATCH 11/55] [Docs] Pull images before starting mailcow --- docs/install.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/install.md b/docs/install.md index fddfaa59..1274ed84 100644 --- a/docs/install.md +++ b/docs/install.md @@ -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. -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 ``` From 509a804acdb0d7eaa5eb2e2cca199a068fb58efc Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 16 Mar 2017 13:42:56 +0100 Subject: [PATCH 12/55] Deprecated lua function replaced, better tag handling when spam --- data/conf/rspamd/lua/rspamd.local.lua | 85 ++++++++++++++------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua index 236734f2..60516991 100644 --- a/data/conf/rspamd/lua/rspamd.local.lua +++ b/data/conf/rspamd/lua/rspamd.local.lua @@ -23,51 +23,56 @@ auth_domain_map = rspamd_config:add_map({ description = 'Map of domains we are authoritative for' }) -rspamd_config:register_post_filter(function(task) - local util = require("rspamd_util") - local rspamd_logger = require "rspamd_logger" +rspamd_config:register_symbol({ + name = 'TAG_MOO', + type = 'postfilter', + callback = function(task) + local util = require("rspamd_util") + local rspamd_logger = require "rspamd_logger" - local tagged_rcpt = task:get_symbol("TAGGED_RCPT") - 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 tagged_rcpt = task:get_symbol("TAGGED_RCPT") + 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) - if tagged_rcpt then - local tag = tagged_rcpt[1].options[1] - rspamd_logger.infox("found tag: %s", tag) - local action = task:get_metric_action('default') - rspamd_logger.infox("metric action now: %s", action) + if tagged_rcpt then + local tag = tagged_rcpt[1].options[1] + rspamd_logger.infox("found tag: %s", tag) + local action = task:get_metric_action('default') + rspamd_logger.infox("metric action now: %s", action) - if action ~= 'no action' and action ~= 'greylist' then - rspamd_logger.infox("skipping tag handler for action: %s", action) + if action ~= 'no action' and action ~= 'greylist' then + rspamd_logger.infox("skipping tag handler for action: %s", action) + task:set_metric_action('default', action) + end + + 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 - - 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) + end, + priority = 10 +}) rspamd_config.MRAPTOR = { callback = function(task) From edf047cf6687e87f0eebad865f3054089bb33583 Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 16 Mar 2017 13:43:04 +0100 Subject: [PATCH 13/55] Fix domain alias edit error --- data/web/inc/functions.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 87bed9b9..9c2bdbc1 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -3320,7 +3320,7 @@ function mailbox_edit_alias_domain($postarray) { $stmt = $pdo->prepare("UPDATE `alias_domain` SET `alias_domain` = :alias_domain, `active` = :active, - `modified` = :modified, + `modified` = :modified WHERE `alias_domain` = :alias_domain_now"); $stmt->execute(array( ':alias_domain' => $alias_domain, From 2fcbce4f2ebfe32a93fde344506f8119edf88264 Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 17 Mar 2017 19:23:25 +0100 Subject: [PATCH 14/55] Fix: return true --- data/conf/rspamd/lua/rspamd.local.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua index 60516991..7d6aaa20 100644 --- a/data/conf/rspamd/lua/rspamd.local.lua +++ b/data/conf/rspamd/lua/rspamd.local.lua @@ -45,6 +45,7 @@ rspamd_config:register_symbol({ if action ~= 'no action' and action ~= 'greylist' then rspamd_logger.infox("skipping tag handler for action: %s", action) task:set_metric_action('default', action) + return true end if authdomain then From 89aef716b48949d58fead3525fdf8fb302866c90 Mon Sep 17 00:00:00 2001 From: tehXor Date: Fri, 17 Mar 2017 20:38:20 +0100 Subject: [PATCH 15/55] - collapsible tables on mailbox.php which are collapsed per default if they are long - changed username field on the login form to the type "text" as "name" is no valid type which causes problems with some extensions (password managers) --- data/web/index.php | 2 +- data/web/js/mailbox.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/data/web/index.php b/data/web/index.php index 31bafda6..e34b4da5 100644 --- a/data/web/index.php +++ b/data/web/index.php @@ -29,7 +29,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
- +
diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index eedd51cb..9bd8ef9b 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -51,4 +51,16 @@ $(document).ready(function() { $panel.find('.panel-body input').focus(); } }); + $('.container').on('click', '.panel-heading .panel-title', function(e){ + var $this = $(this), + $panel = $this.parents('.panel'); + $panel.find('.table-responsive').slideToggle("fast"); + }); + $('.panel-heading .panel-title').addClass('clickable'); + $('.panel .table-responsive').each(function() { + if ($(this).height() > 550) { + // If one is too large initially hide all + $('.panel .table-responsive').slideUp("fast"); + } + }) }); From 7e6fc8568f6950ee230364f4b18de7fec365ad6f Mon Sep 17 00:00:00 2001 From: andryyy Date: Sun, 19 Mar 2017 21:55:03 +0100 Subject: [PATCH 16/55] Move DKIM signing to new dkim_signing.conf --- data/conf/rspamd/local.d/dkim.conf | 34 ------------------- data/conf/rspamd/local.d/dkim_signing.conf | 38 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 34 deletions(-) delete mode 100644 data/conf/rspamd/local.d/dkim.conf create mode 100644 data/conf/rspamd/local.d/dkim_signing.conf diff --git a/data/conf/rspamd/local.d/dkim.conf b/data/conf/rspamd/local.d/dkim.conf deleted file mode 100644 index c199c6ae..00000000 --- a/data/conf/rspamd/local.d/dkim.conf +++ /dev/null @@ -1,34 +0,0 @@ -sign_condition =< Date: Mon, 20 Mar 2017 21:38:52 +0100 Subject: [PATCH 17/55] Fix Postfix sender when sender is alias in alias domain --- data/Dockerfiles/postfix/postfix.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/Dockerfiles/postfix/postfix.sh b/data/Dockerfiles/postfix/postfix.sh index 8c079405..c628e771 100755 --- a/data/Dockerfiles/postfix/postfix.sh +++ b/data/Dockerfiles/postfix/postfix.sh @@ -81,7 +81,7 @@ user = ${DBUSER} password = ${DBPASS} hosts = mysql 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 cat < /opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf From 51230f780f061a66626b9c4ec3b2cdefebc6fa0f Mon Sep 17 00:00:00 2001 From: root Date: Tue, 21 Mar 2017 08:11:06 +0100 Subject: [PATCH 18/55] Local --- data/conf/rspamd/local.d/ratelimit.conf | 8 + data/web/css/footable.bootstrap.min.css | 1 + data/web/js/footable.min.js | 10 + data/web/js/mailbox.js | 156 ++++++--- data/web/mailbox.php | 402 +++--------------------- 5 files changed, 161 insertions(+), 416 deletions(-) create mode 100644 data/conf/rspamd/local.d/ratelimit.conf create mode 100644 data/web/css/footable.bootstrap.min.css create mode 100644 data/web/js/footable.min.js diff --git a/data/conf/rspamd/local.d/ratelimit.conf b/data/conf/rspamd/local.d/ratelimit.conf new file mode 100644 index 00000000..36234214 --- /dev/null +++ b/data/conf/rspamd/local.d/ratelimit.conf @@ -0,0 +1,8 @@ +rates { + to = [1100, 0.033333333]; + to_ip = [30, 0.025]; + to_ip_from = [20, 0.01666666667]; + bounce_to = [10, 0.000555556]; + bounce_to_ip = [5, 0.000277778]; + user = [20, 0.01666666667]; +} diff --git a/data/web/css/footable.bootstrap.min.css b/data/web/css/footable.bootstrap.min.css new file mode 100644 index 00000000..a030a7ff --- /dev/null +++ b/data/web/css/footable.bootstrap.min.css @@ -0,0 +1 @@ +table.footable-details,table.footable>thead>tr.footable-filtering>th div.form-group{margin-bottom:0}table.footable,table.footable-details{position:relative;width:100%;border-spacing:0;border-collapse:collapse}table.footable-hide-fouc{display:none}table>tbody>tr>td>span.footable-toggle{margin-right:8px;opacity:.3}table>tbody>tr>td>span.footable-toggle.last-column{margin-left:8px;float:right}table.table-condensed>tbody>tr>td>span.footable-toggle{margin-right:5px}table.footable-details>tbody>tr>th:nth-child(1){min-width:40px;width:120px}table.footable-details>tbody>tr>td:nth-child(2){word-break:break-all}table.footable-details>tbody>tr:first-child>td,table.footable-details>tbody>tr:first-child>th,table.footable-details>tfoot>tr:first-child>td,table.footable-details>tfoot>tr:first-child>th,table.footable-details>thead>tr:first-child>td,table.footable-details>thead>tr:first-child>th{border-top-width:0}table.footable-details.table-bordered>tbody>tr:first-child>td,table.footable-details.table-bordered>tbody>tr:first-child>th,table.footable-details.table-bordered>tfoot>tr:first-child>td,table.footable-details.table-bordered>tfoot>tr:first-child>th,table.footable-details.table-bordered>thead>tr:first-child>td,table.footable-details.table-bordered>thead>tr:first-child>th{border-top-width:1px}div.footable-loader{vertical-align:middle;text-align:center;height:300px;position:relative}div.footable-loader>span.fooicon{display:inline-block;opacity:.3;font-size:30px;line-height:32px;width:32px;height:32px;margin-top:-16px;margin-left:-16px;position:absolute;top:50%;left:50%;-webkit-animation:fooicon-spin-r 2s infinite linear;animation:fooicon-spin-r 2s infinite linear}table.footable>tbody>tr.footable-empty>td{vertical-align:middle;text-align:center;font-size:30px}table.footable>tbody>tr>td,table.footable>tbody>tr>th{display:none}table.footable>tbody>tr.footable-detail-row>td,table.footable>tbody>tr.footable-detail-row>th,table.footable>tbody>tr.footable-empty>td,table.footable>tbody>tr.footable-empty>th{display:table-cell}@-webkit-keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fooicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings'!important;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fooicon:after,.fooicon:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fooicon-loader:before{content:"\e030"}.fooicon-plus:before{content:"\2b"}.fooicon-minus:before{content:"\2212"}.fooicon-search:before{content:"\e003"}.fooicon-remove:before{content:"\e014"}.fooicon-sort:before{content:"\e150"}.fooicon-sort-asc:before{content:"\e155"}.fooicon-sort-desc:before{content:"\e156"}.fooicon-pencil:before{content:"\270f"}.fooicon-trash:before{content:"\e020"}.fooicon-eye-close:before{content:"\e106"}.fooicon-flash:before{content:"\e162"}.fooicon-cog:before{content:"\e019"}.fooicon-stats:before{content:"\e185"}table.footable>thead>tr.footable-filtering>th{border-bottom-width:1px;font-weight:400}table.footable.footable-filtering-right>thead>tr.footable-filtering>th,table.footable>thead>tr.footable-filtering>th{text-align:right}table.footable.footable-filtering-left>thead>tr.footable-filtering>th{text-align:left}table.footable-paging-center>tfoot>tr.footable-paging>td,table.footable.footable-filtering-center>thead>tr.footable-filtering>th,table.footable>tfoot>tr.footable-paging>td{text-align:center}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:5px}table.footable>thead>tr.footable-filtering>th div.input-group{width:100%}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox{margin:0;display:block;position:relative}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox>label{display:block;padding-left:20px}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox input[type=checkbox]{position:absolute;margin-left:-20px}@media (min-width:768px){table.footable>thead>tr.footable-filtering>th div.input-group{width:auto}table.footable>thead>tr.footable-filtering>th div.form-group{margin-left:2px;margin-right:2px}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:0}}table.footable>tbody>tr>td.footable-sortable,table.footable>tbody>tr>th.footable-sortable,table.footable>tfoot>tr>td.footable-sortable,table.footable>tfoot>tr>th.footable-sortable,table.footable>thead>tr>td.footable-sortable,table.footable>thead>tr>th.footable-sortable{position:relative;padding-right:30px;cursor:pointer}td.footable-sortable>span.fooicon,th.footable-sortable>span.fooicon{position:absolute;right:6px;top:50%;margin-top:-7px;opacity:0;transition:opacity .3s ease-in}td.footable-sortable.footable-asc>span.fooicon,td.footable-sortable.footable-desc>span.fooicon,td.footable-sortable:hover>span.fooicon,th.footable-sortable.footable-asc>span.fooicon,th.footable-sortable.footable-desc>span.fooicon,th.footable-sortable:hover>span.fooicon{opacity:1}table.footable-sorting-disabled td.footable-sortable.footable-asc>span.fooicon,table.footable-sorting-disabled td.footable-sortable.footable-desc>span.fooicon,table.footable-sorting-disabled td.footable-sortable:hover>span.fooicon,table.footable-sorting-disabled th.footable-sortable.footable-asc>span.fooicon,table.footable-sorting-disabled th.footable-sortable.footable-desc>span.fooicon,table.footable-sorting-disabled th.footable-sortable:hover>span.fooicon{opacity:0;visibility:hidden}table.footable>tfoot>tr.footable-paging>td>ul.pagination{margin:10px 0 0}table.footable>tfoot>tr.footable-paging>td>span.label{display:inline-block;margin:0 0 10px;padding:4px 10px}table.footable-paging-left>tfoot>tr.footable-paging>td{text-align:left}table.footable-editing-right td.footable-editing,table.footable-editing-right tr.footable-editing,table.footable-paging-right>tfoot>tr.footable-paging>td{text-align:right}ul.pagination>li.footable-page{display:none}ul.pagination>li.footable-page.visible{display:inline}td.footable-editing{width:90px;max-width:90px}table.footable-editing-no-delete td.footable-editing,table.footable-editing-no-edit td.footable-editing,table.footable-editing-no-view td.footable-editing{width:70px;max-width:70px}table.footable-editing-no-delete.footable-editing-no-view td.footable-editing,table.footable-editing-no-edit.footable-editing-no-delete td.footable-editing,table.footable-editing-no-edit.footable-editing-no-view td.footable-editing{width:50px;max-width:50px}table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view td.footable-editing,table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view th.footable-editing{width:0;max-width:0;display:none!important}table.footable-editing-left td.footable-editing,table.footable-editing-left tr.footable-editing{text-align:left}table.footable-editing button.footable-add,table.footable-editing button.footable-hide,table.footable-editing-show button.footable-show,table.footable-editing.footable-editing-always-show button.footable-hide,table.footable-editing.footable-editing-always-show button.footable-show,table.footable-editing.footable-editing-always-show.footable-editing-no-add tr.footable-editing{display:none}table.footable-editing.footable-editing-always-show button.footable-add,table.footable-editing.footable-editing-show button.footable-add,table.footable-editing.footable-editing-show button.footable-hide{display:inline-block} \ No newline at end of file diff --git a/data/web/js/footable.min.js b/data/web/js/footable.min.js new file mode 100644 index 00000000..131c8871 --- /dev/null +++ b/data/web/js/footable.min.js @@ -0,0 +1,10 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +!function(a,b){window.console=window.console||{log:function(){},error:function(){}},a.fn.footable=function(a,c){return a=a||{},this.filter("table").each(function(d,e){b.init(e,a,c)})};var c={events:[]};b.__debug__=JSON.parse(localStorage.getItem("footable_debug"))||!1,b.__debug_options__=JSON.parse(localStorage.getItem("footable_debug_options"))||c,b.debug=function(d,e){return b.is["boolean"](d)?(b.__debug__=d,void(b.__debug__?(localStorage.setItem("footable_debug",JSON.stringify(b.__debug__)),b.__debug_options__=a.extend(!0,{},c,e||{}),b.is.hash(e)&&localStorage.setItem("footable_debug_options",JSON.stringify(b.__debug_options__))):(localStorage.removeItem("footable_debug"),localStorage.removeItem("footable_debug_options")))):b.__debug__},b.get=function(b){return a(b).first().data("__FooTable__")},b.init=function(a,c,d){var e=b.get(a);return e instanceof b.Table&&e.destroy(),new b.Table(a,c,d)},b.getRow=function(b){var c=a(b).closest("tr");return c.hasClass("footable-detail-row")&&(c=c.prev()),c.data("__FooTableRow__")}}(jQuery,FooTable=window.FooTable||{}),function(a){var b=function(){return!0};a.arr={},a.arr.each=function(b,c){if(a.is.array(b)&&a.is.fn(c))for(var d=0,e=b.length;e>d&&c(b[d],d)!==!1;d++);},a.arr.get=function(b,c){var d=[];if(!a.is.array(b))return d;if(!a.is.fn(c))return b;for(var e=0,f=b.length;f>e;e++)c(b[e],e)&&d.push(b[e]);return d},a.arr.any=function(c,d){if(!a.is.array(c))return!1;d=a.is.fn(d)?d:b;for(var e=0,f=c.length;f>e;e++)if(d(c[e],e))return!0;return!1},a.arr.contains=function(b,c){if(!a.is.array(b)||a.is.undef(c))return!1;for(var d=0,e=b.length;e>d;d++)if(b[d]==c)return!0;return!1},a.arr.first=function(c,d){if(!a.is.array(c))return null;d=a.is.fn(d)?d:b;for(var e=0,f=c.length;f>e;e++)if(d(c[e],e))return c[e];return null},a.arr.map=function(b,c){var d=[],e=null;if(!a.is.array(b)||!a.is.fn(c))return d;for(var f=0,g=b.length;g>f;f++)null!=(e=c(b[f],f))&&d.push(e);return d},a.arr.remove=function(b,c){var d=[],e=[];if(!a.is.array(b)||!a.is.fn(c))return e;for(var f=0,g=b.length;g>f;f++)c(b[f],f,e)&&(d.push(f),e.push(b[f]));for(d.sort(function(a,b){return b-a}),f=0,g=d.length;g>f;f++){var h=d[f]-f;b.splice(h,1)}return e},a.arr["delete"]=function(b,c){var d=-1,e=null;if(!a.is.array(b)||a.is.undef(c))return e;for(var f=0,g=b.length;g>f;f++)if(b[f]==c){d=f,e=b[f];break}return-1!=d&&b.splice(d,1),e},a.arr.replace=function(a,b,c){var d=a.indexOf(b);-1!==d&&(a[d]=c)}}(FooTable),function(a){a.is={},a.is.type=function(a,b){return typeof a===b},a.is.defined=function(a){return"undefined"!=typeof a},a.is.undef=function(a){return"undefined"==typeof a},a.is.array=function(a){return"[object Array]"===Object.prototype.toString.call(a)},a.is.date=function(a){return"[object Date]"===Object.prototype.toString.call(a)&&!isNaN(a.getTime())},a.is["boolean"]=function(a){return"[object Boolean]"===Object.prototype.toString.call(a)},a.is.string=function(a){return"[object String]"===Object.prototype.toString.call(a)},a.is.number=function(a){return"[object Number]"===Object.prototype.toString.call(a)&&!isNaN(a)},a.is.fn=function(b){return a.is.defined(window)&&b===window.alert||"[object Function]"===Object.prototype.toString.call(b)},a.is.error=function(a){return"[object Error]"===Object.prototype.toString.call(a)},a.is.object=function(a){return"[object Object]"===Object.prototype.toString.call(a)},a.is.hash=function(b){return a.is.object(b)&&b.constructor===Object&&!b.nodeType&&!b.setInterval},a.is.element=function(a){return"object"==typeof HTMLElement?a instanceof HTMLElement:a&&"object"==typeof a&&null!==a&&1===a.nodeType&&"string"==typeof a.nodeName},a.is.promise=function(b){return a.is.object(b)&&a.is.fn(b.then)&&a.is.fn(b.promise)},a.is.jq=function(b){return a.is.defined(window.jQuery)&&b instanceof jQuery&&b.length>0},a.is.moment=function(b){return a.is.defined(window.moment)&&a.is.object(b)&&a.is["boolean"](b._isAMomentObject)},a.is.emptyObject=function(b){if(!a.is.hash(b))return!1;for(var c in b)if(b.hasOwnProperty(c))return!1;return!0},a.is.emptyArray=function(b){return a.is.array(b)?0===b.length:!0},a.is.emptyString=function(b){return a.is.string(b)?0===b.length:!0}}(FooTable),function(a){a.str={},a.str.contains=function(b,c,d){return a.is.emptyString(b)||a.is.emptyString(c)?!1:c.length<=b.length&&-1!==(d?b.toUpperCase().indexOf(c.toUpperCase()):b.indexOf(c))},a.str.containsExact=function(b,c,d){return a.is.emptyString(b)||a.is.emptyString(c)||c.length>b.length?!1:new RegExp("\\b"+a.str.escapeRegExp(c)+"\\b",d?"i":"").test(b)},a.str.containsWord=function(b,c,d){if(a.is.emptyString(b)||a.is.emptyString(c)||b.lengthf;f++)if(d?e[f].toUpperCase()==c.toUpperCase():e[f]==c)return!0;return!1},a.str.from=function(b,c){return a.is.emptyString(b)?b:a.str.contains(b,c)?b.substring(b.indexOf(c)+1):b},a.str.startsWith=function(b,c){return a.is.emptyString(b)?b==c:b.slice(0,c.length)==c},a.str.toCamelCase=function(b){return a.is.emptyString(b)?b:b.toUpperCase()===b?b.toLowerCase():b.replace(/^([A-Z])|[-\s_](\w)/g,function(b,c,d){return a.is.string(d)?d.toUpperCase():c.toLowerCase()})},a.str.random=function(b){return b=a.is.emptyString(b)?"":b,b+Math.random().toString(36).substr(2,9)},a.str.escapeRegExp=function(b){return a.is.emptyString(b)?b:b.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}}(FooTable),function(a){"use strict";function b(){}Object.create||(Object.create=function(){var b=function(){};return function(c){if(arguments.length>1)throw Error("Second argument not supported");if(!a.is.object(c))throw TypeError("Argument must be an object");b.prototype=c;var d=new b;return b.prototype=null,d}}());var c=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;b.__extend__=function(b,d,e,f){b[d]=a.is.fn(f)&&c.test(e)?function(a,b){return function(){var a,c;return a=this._super,this._super=f,c=b.apply(this,arguments),this._super=a,c}}(d,e):e},b.extend=function(d,e){function f(b,d,e,f){b[d]=a.is.fn(f)&&c.test(e)?function(a,b,c){return function(){var a,d;return a=this._super,this._super=c,d=b.apply(this,arguments),this._super=a,d}}(d,e,f):e}var g=Array.prototype.slice.call(arguments);if(d=g.shift(),e=g.shift(),a.is.hash(d)){var h=Object.create(this.prototype),i=this.prototype;for(var j in d)"__ctor__"!==j&&f(h,j,d[j],i[j]);var k=a.is.fn(h.__ctor__)?h.__ctor__:function(){if(!a.is.fn(this.construct))throw new SyntaxError('FooTable class objects must be constructed with the "new" keyword.');this.construct.apply(this,arguments)};return h.construct=a.is.fn(h.construct)?h.construct:function(){},k.prototype=h,h.constructor=k,k.extend=b.extend,k}a.is.string(d)&&a.is.fn(e)&&f(this.prototype,d,e,this.prototype[d])},a.Class=b,a.ClassFactory=a.Class.extend({construct:function(){this.registered={}},contains:function(b){return a.is.defined(this.registered[b])},names:function(){var a,b=[];for(a in this.registered)this.registered.hasOwnProperty(a)&&b.push(a);return b},register:function(b,c,d){if(a.is.string(b)&&a.is.fn(c)){var e=this.registered[b];this.registered[b]={name:b,klass:c,priority:a.is.number(d)?d:a.is.defined(e)?e.priority:0}}},load:function(b,c,d){var e,f,g=this,h=Array.prototype.slice.call(arguments),i=[],j=[];b=h.shift()||{};for(e in g.registered)if(g.registered.hasOwnProperty(e)){var k=g.registered[e];b.hasOwnProperty(e)&&(f=b[e],a.is.string(f)&&(f=a.getFnPointer(b[e])),a.is.fn(f)&&(k={name:e,klass:f,priority:g.registered[e].priority})),i.push(k)}for(e in b)b.hasOwnProperty(e)&&!g.registered.hasOwnProperty(e)&&(f=b[e],a.is.string(f)&&(f=a.getFnPointer(b[e])),a.is.fn(f)&&i.push({name:e,klass:f,priority:0}));return i.sort(function(a,b){return b.priority-a.priority}),a.arr.each(i,function(b){a.is.fn(b.klass)&&j.push(g._make(b.klass,h))}),j},make:function(b,c,d){var e,f=this,g=Array.prototype.slice.call(arguments);return b=g.shift(),e=f.registered[b],a.is.fn(e.klass)?f._make(e.klass,g):null},_make:function(a,b){function c(){return a.apply(this,b)}return c.prototype=a.prototype,new c}})}(FooTable),function(a,b){b.css2json=function(c){if(b.is.emptyString(c))return{};for(var d,e,f,g={},h=c.split(";"),i=0,j=h.length;j>i;i++)b.is.emptyString(h[i])||(d=h[i].split(":"),b.is.emptyString(d[0])||b.is.emptyString(d[1])||(e=b.str.toCamelCase(a.trim(d[0])),f=a.trim(d[1]),g[e]=f));return g},b.getFnPointer=function(a){if(b.is.emptyString(a))return null;var c=window,d=a.split(".");return b.arr.each(d,function(a){c[a]&&(c=c[a])}),b.is.fn(c)?c:null},b.checkFnValue=function(a,c,d){function e(a,c,d){return b.is.fn(c)?function(){return c.apply(a,arguments)}:d}return d=b.is.fn(d)?d:null,b.is.fn(c)?e(a,c,d):b.is.type(c,"string")?e(a,b.getFnPointer(c),d):d}}(jQuery,FooTable),function(a,b){b.Cell=b.Class.extend({construct:function(a,b,c,d){this.ft=a,this.row=b,this.column=c,this.created=!1,this.define(d)},define:function(c){this.$el=b.is.element(c)||b.is.jq(c)?a(c):null,this.$detail=null;var d=b.is.hash(c)&&b.is.hash(c.options)&&b.is.defined(c.value);this.value=this.column.parser.call(this.column,b.is.jq(this.$el)?this.$el:d?c.value:c,this.ft.o),this.o=a.extend(!0,{classes:null,style:null},d?c.options:{}),this.classes=b.is.jq(this.$el)&&this.$el.attr("class")?this.$el.attr("class").match(/\S+/g):b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.jq(this.$el)&&this.$el.attr("style")?b.css2json(this.$el.attr("style")):b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{}},$create:function(){this.created||((this.$el=b.is.jq(this.$el)?this.$el:a("")).data("value",this.value).contents().detach().end().append(this.format(this.value)),this._setClasses(this.$el),this._setStyle(this.$el),this.$detail=a("").addClass(this.row.classes.join(" ")).data("__FooTableCell__",this).append(a("")).append(a("")),this.created=!0)},collapse:function(){this.created&&(this.$detail.children("th").html(this.column.title),this.$el.clone().attr("id",this.$el.attr("id")?this.$el.attr("id")+"-detail":void 0).css("display","table-cell").html("").append(this.$el.contents().detach()).replaceAll(this.$detail.children("td").first()),b.is.jq(this.$detail.parent())||this.$detail.appendTo(this.row.$details.find(".footable-details > tbody")))},restore:function(){if(this.created){if(b.is.jq(this.$detail.parent())){var a=this.$detail.children("td").first();this.$el.attr("class",a.attr("class")).attr("style",a.attr("style")).css("display",this.column.hidden||!this.column.visible?"none":"table-cell").append(a.contents().detach())}this.$detail.detach()}},parse:function(){return this.column.parser.call(this.column,this.$el,this.ft.o)},format:function(a){return this.column.formatter.call(this.column,a,this.ft.o)},val:function(c,d){if(b.is.undef(c))return this.value;var e=this,f=b.is.hash(c)&&b.is.hash(c.options)&&b.is.defined(c.value);if(this.o=a.extend(!0,{classes:e.classes,style:e.style},f?c.options:{}),this.value=f?c.value:c,this.classes=b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},this.created){this.$el.data("value",this.value).empty();var g=this.$detail.children("td").first().empty(),h=b.is.jq(this.$detail.parent())?g:this.$el;h.append(this.format(this.value)),this._setClasses(h),this._setStyle(h),(b.is["boolean"](d)?d:!0)&&this.row.draw()}},_setClasses:function(a){var c=!b.is.emptyArray(this.column.classes),d=!b.is.emptyArray(this.classes),e=null;a.removeAttr("class"),(c||d)&&(c&&d?e=this.classes.concat(this.column.classes).join(" "):c?e=this.column.classes.join(" "):d&&(e=this.classes.join(" ")),b.is.emptyString(e)||a.addClass(e))},_setStyle:function(c){var d=!b.is.emptyObject(this.column.style),e=!b.is.emptyObject(this.style),f=null;c.removeAttr("style"),(d||e)&&(d&&e?f=a.extend({},this.column.style,this.style):d?f=this.column.style:e&&(f=this.style),b.is.hash(f)&&c.css(f))}})}(jQuery,FooTable),function(a,b){b.Column=b.Class.extend({construct:function(a,c,d){this.ft=a,this.type=b.is.emptyString(d)?"text":d,this.virtual=b.is["boolean"](c.virtual)?c.virtual:!1,this.$el=b.is.jq(c.$el)?c.$el:null,this.index=b.is.number(c.index)?c.index:-1,this.define(c),this.$create()},define:function(a){this.hidden=b.is["boolean"](a.hidden)?a.hidden:!1,this.visible=b.is["boolean"](a.visible)?a.visible:!0,this.name=b.is.string(a.name)?a.name:null,null==this.name&&(this.name="col"+(a.index+1)),this.title=b.is.string(a.title)?a.title:null,!this.virtual&&null==this.title&&b.is.jq(this.$el)&&(this.title=this.$el.html()),null==this.title&&(this.title="Column "+(a.index+1)),this.style=b.is.hash(a.style)?a.style:b.is.string(a.style)?b.css2json(a.style):{},this.classes=b.is.array(a.classes)?a.classes:b.is.string(a.classes)?a.classes.match(/\S+/g):[],this.parser=b.checkFnValue(this,a.parser,this.parser),this.formatter=b.checkFnValue(this,a.formatter,this.formatter)},$create:function(){(this.$el=!this.virtual&&b.is.jq(this.$el)?this.$el:a("")).html(this.title).addClass(this.classes.join(" ")).css(this.style)},parser:function(c){if(b.is.element(c)||b.is.jq(c)){var d=a(c).data("value");return b.is.defined(d)?d:a(c).html()}return b.is.defined(c)&&null!=c?c+"":null},formatter:function(a){return null==a?"":a},createCell:function(a){var c=b.is.jq(a.$el)?a.$el.children("td,th").get(this.index):null,d=b.is.hash(a.value)?a.value[this.name]:null;return new b.Cell(this.ft,a,this,c||d)}}),b.columns=new b.ClassFactory,b.columns.register("text",b.Column)}(jQuery,FooTable),function(a,b){b.Component=b.Class.extend({construct:function(a,c){if(!(a instanceof b.Table))throw new TypeError("The instance parameter must be an instance of FooTable.Table.");this.ft=a,this.enabled=b.is["boolean"](c)?c:!1},preinit:function(a){},init:function(){},destroy:function(){},predraw:function(){},draw:function(){},postdraw:function(){}}),b.components=new b.ClassFactory}(jQuery,FooTable),function(a,b){b.Defaults=function(){this.stopPropagation=!1,this.on=null},b.defaults=new b.Defaults}(jQuery,FooTable),function(a,b){b.Row=b.Class.extend({construct:function(a,b,c){this.ft=a,this.columns=b,this.created=!1,this.define(c)},define:function(c){this.$el=b.is.element(c)||b.is.jq(c)?a(c):null,this.$toggle=a("",{"class":"footable-toggle fooicon fooicon-plus"});var d=b.is.hash(c),e=d&&b.is.hash(c.options)&&b.is.hash(c.value);this.value=d?e?c.value:c:null,this.o=a.extend(!0,{expanded:!1,classes:null,style:null},e?c.options:{}),this.expanded=b.is.jq(this.$el)?this.$el.data("expanded")||this.o.expanded:this.o.expanded,this.classes=b.is.jq(this.$el)&&this.$el.attr("class")?this.$el.attr("class").match(/\S+/g):b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.jq(this.$el)&&this.$el.attr("style")?b.css2json(this.$el.attr("style")):b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},this.cells=this.createCells();var f=this;f.value={},b.arr.each(f.cells,function(a){f.value[a.column.name]=a.val()})},$create:function(){if(!this.created){(this.$el=b.is.jq(this.$el)?this.$el:a("")).data("__FooTableRow__",this),this._setClasses(this.$el),this._setStyle(this.$el),"last"==this.ft.rows.toggleColumn&&this.$toggle.addClass("last-column"),this.$details=a("",{"class":"footable-detail-row"}).append(a("",{colspan:this.ft.columns.visibleColspan}).append(a("",{"class":"footable-details "+this.ft.classes.join(" ")}).append("")));var c=this;b.arr.each(c.cells,function(a){a.created||a.$create(),c.$el.append(a.$el)}),c.$el.off("click.ft.row").on("click.ft.row",{self:c},c._onToggle),this.created=!0}},createCells:function(){var a=this;return b.arr.map(a.columns,function(b){return b.createCell(a)})},val:function(c,d){var e=this;if(!b.is.hash(c))return b.is.hash(this.value)&&!b.is.emptyObject(this.value)||(this.value={},b.arr.each(this.cells,function(a){e.value[a.column.name]=a.val()})),this.value;this.collapse(!1);var f=b.is.hash(c),g=f&&b.is.hash(c.options)&&b.is.hash(c.value);if(this.o=a.extend(!0,{expanded:e.expanded,classes:e.classes,style:e.style},g?c.options:{}),this.expanded=this.o.expanded,this.classes=b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},f)if(g&&(c=c.value),b.is.hash(this.value))for(var h in c)c.hasOwnProperty(h)&&(this.value[h]=c[h]);else this.value=c;else this.value=null;b.arr.each(this.cells,function(a){b.is.defined(e.value[a.column.name])&&a.val(e.value[a.column.name],!1)}),this.created&&(this._setClasses(this.$el),this._setStyle(this.$el),(b.is["boolean"](d)?d:!0)&&this.draw())},_setClasses:function(a){var c=!b.is.emptyArray(this.classes),d=null;a.removeAttr("class"),c&&(d=this.classes.join(" "),b.is.emptyString(d)||a.addClass(d))},_setStyle:function(a){var c=!b.is.emptyObject(this.style),d=null;a.removeAttr("style"),c&&(d=this.style,b.is.hash(d)&&a.css(d))},expand:function(){if(this.created){var a=this;a.ft.raise("expand.ft.row",[a]).then(function(){a.__hidden__=b.arr.map(a.cells,function(a){return a.column.hidden&&a.column.visible?a:null}),a.__hidden__.length>0&&(a.$details.insertAfter(a.$el).children("td").first().attr("colspan",a.ft.columns.visibleColspan),b.arr.each(a.__hidden__,function(a){a.collapse()})),a.$el.attr("data-expanded",!0),a.$toggle.removeClass("fooicon-plus").addClass("fooicon-minus"),a.expanded=!0})}},collapse:function(a){if(this.created){var c=this;c.ft.raise("collapse.ft.row",[c]).then(function(){b.arr.each(c.__hidden__,function(a){a.restore()}),c.$details.detach(),c.$el.removeAttr("data-expanded"),c.$toggle.removeClass("fooicon-minus").addClass("fooicon-plus"),(b.is["boolean"](a)?a:!0)&&(c.expanded=!1)})}},predraw:function(a){this.created&&(this.expanded&&this.collapse(!1),this.$toggle.detach(),a=b.is["boolean"](a)?a:!0,a&&this.$el.detach())},draw:function(a){this.created||this.$create(),b.is.jq(a)&&a.append(this.$el);var c=this;b.arr.each(c.cells,function(a){a.$el.css("display",a.column.hidden||!a.column.visible?"none":"table-cell"),c.ft.rows.showToggle&&c.ft.columns.hasHidden&&("first"==c.ft.rows.toggleColumn&&a.column.index==c.ft.columns.firstVisibleIndex||"last"==c.ft.rows.toggleColumn&&a.column.index==c.ft.columns.lastVisibleIndex)&&a.$el.prepend(c.$toggle),a.$el.add(a.column.$el).removeClass("footable-first-visible footable-last-visible"),a.column.index==c.ft.columns.firstVisibleIndex&&a.$el.add(a.column.$el).addClass("footable-first-visible"),a.column.index==c.ft.columns.lastVisibleIndex&&a.$el.add(a.column.$el).addClass("footable-last-visible")}),this.expanded&&this.expand()},toggle:function(){this.created&&this.ft.columns.hasHidden&&(this.expanded?this.collapse():this.expand())},_onToggle:function(b){var c=b.data.self;a(b.target).is(c.ft.rows.toggleSelector)&&c.toggle()}})}(jQuery,FooTable),function(a,b){b.instances=[],b.Table=b.Class.extend({construct:function(c,d,e){this._resizeTimeout=null,this.id=b.instances.push(this),this.initialized=!1,this.$el=(b.is.jq(c)?c:a(c)).first(),this.$loader=a("
",{"class":"footable-loader"}).append(a("",{"class":"fooicon fooicon-loader"})),this.o=a.extend(!0,{},b.defaults,d),this.data=this.$el.data()||{},this.classes=[],this.components=b.components.load(b.is.hash(this.data.components)?this.data.components:this.o.components,this),this.breakpoints=this.use(FooTable.Breakpoints),this.columns=this.use(FooTable.Columns),this.rows=this.use(FooTable.Rows),this._construct(e)},_construct:function(a){var c=this;this._preinit().then(function(){return c._init()}).always(function(d){return c.$el.show(),b.is.error(d)?void console.error("FooTable: unhandled error thrown during initialization.",d):c.raise("ready.ft.table").then(function(){b.is.fn(a)&&a.call(c,c)})})},_preinit:function(){var a=this;return this.raise("preinit.ft.table",[a.data]).then(function(){var c=(a.$el.attr("class")||"").match(/\S+/g)||[];a.o.ajax=b.checkFnValue(a,a.data.ajax,a.o.ajax),a.o.stopPropagation=b.is["boolean"](a.data.stopPropagation)?a.data.stopPropagation:a.o.stopPropagation;for(var d=0,e=c.length;e>d;d++)b.str.startsWith(c[d],"footable")||a.classes.push(c[d]);return a.$el.hide().after(a.$loader),a.execute(!1,!1,"preinit",a.data)})},_init:function(){var c=this;return c.raise("init.ft.table").then(function(){var d=c.$el.children("thead"),e=c.$el.children("tbody"),f=c.$el.children("tfoot");return c.$el.addClass("footable footable-"+c.id),b.is.hash(c.o.on)&&c.$el.on(c.o.on),0==f.length&&c.$el.append(f=a("
")),0==e.length&&c.$el.append(""),0==d.length&&c.$el.prepend(d=a("")),c.execute(!1,!0,"init").then(function(){return c.$el.data("__FooTable__",c),0==f.children("tr").length&&f.remove(),0==d.children("tr").length&&d.remove(),c.raise("postinit.ft.table").then(function(){return c.draw()}).always(function(){a(window).off("resize.ft"+c.id,c._onWindowResize).on("resize.ft"+c.id,{self:c},c._onWindowResize),c.initialized=!0})})})},destroy:function(){var c=this;return c.raise("destroy.ft.table").then(function(){return c.execute(!0,!0,"destroy").then(function(){c.$el.removeData("__FooTable__").removeClass("footable-"+c.id),b.is.hash(c.o.on)&&c.$el.off(c.o.on),a(window).off("resize.ft"+c.id,c._onWindowResize),c.initialized=!1})}).fail(function(a){b.is.error(a)&&console.error("FooTable: unhandled error thrown while destroying the plugin.",a)})},raise:function(c,d){var e=this,f=b.__debug__&&(b.is.emptyArray(b.__debug_options__.events)||b.arr.any(b.__debug_options__.events,function(a){return b.str.contains(c,a)}));return d=d||[],d.unshift(this),a.Deferred(function(b){var g=a.Event(c);1==e.o.stopPropagation&&e.$el.one(c,function(a){a.stopPropagation()}),f&&console.log("FooTable:"+c+": ",d),e.$el.trigger(g,d),g.isDefaultPrevented()?(f&&console.log('FooTable: default prevented for the "'+c+'" event.'),b.reject(g)):b.resolve(g)})},use:function(a){for(var b=0,c=this.components.length;c>b;b++)if(this.components[b]instanceof a)return this.components[b];return null},draw:function(){var a=this,c=a.$el.clone().insertBefore(a.$el);return a.$el.detach(),a.execute(!1,!0,"predraw").then(function(){return a.raise("predraw.ft.table").then(function(){return a.execute(!1,!0,"draw").then(function(){return a.raise("draw.ft.table").then(function(){return a.execute(!1,!0,"postdraw").then(function(){return a.raise("postdraw.ft.table")})})})})}).fail(function(a){b.is.error(a)&&console.error("FooTable: unhandled error thrown during a draw operation.",a)}).always(function(){c.replaceWith(a.$el),a.$loader.remove()})},execute:function(a,c,d,e,f){var g=this,h=Array.prototype.slice.call(arguments);a=h.shift(),c=h.shift();var i=c?b.arr.get(g.components,function(a){return a.enabled}):g.components.slice(0);return h.unshift(a?i.reverse():i),g._execute.apply(g,h)},_execute:function(c,d,e,f){if(!c||!c.length)return a.when();var g,h=this,i=Array.prototype.slice.call(arguments);return c=i.shift(),d=i.shift(),g=c.shift(),b.is.fn(g[d])?a.Deferred(function(a){try{var c=g[d].apply(g,i);if(b.is.promise(c))return c.then(a.resolve,a.reject);a.resolve(c)}catch(e){a.reject(e)}}).then(function(){return h._execute.apply(h,[c,d].concat(i))}):h._execute.apply(h,[c,d].concat(i))},_onWindowResize:function(a){var b=a.data.self;null!=b._resizeTimeout&&clearTimeout(b._resizeTimeout),b._resizeTimeout=setTimeout(function(){b._resizeTimeout=null,b.raise("resize.ft.table").then(function(){b.breakpoints.check()})},300)}})}(jQuery,FooTable),function(a,b){b.is.undef(window.moment)||(b.DateColumn=b.Column.extend({construct:function(a,c){this._super(a,c,"date"),this.formatString=b.is.string(c.formatString)?c.formatString:"MM-DD-YYYY"},parser:function(c){if(b.is.element(c)||b.is.jq(c)){var d=a(c).data("value");c=b.is.defined(d)?d:a(c).text(),b.is.string(c)&&(c=isNaN(c)?c:+c)}if(b.is.date(c))return moment(c);if(b.is.object(c)&&b.is["boolean"](c._isAMomentObject))return c;if(b.is.string(c)){if(isNaN(c))return moment(c,this.formatString);c=+c}return b.is.number(c)?moment(c):null},formatter:function(a){return b.is.object(a)&&b.is["boolean"](a._isAMomentObject)&&a.isValid()?a.format(this.formatString):""},filterValue:function(c){if((b.is.element(c)||b.is.jq(c))&&(c=a(c).data("filterValue")||a(c).text()),b.is.hash(c)&&b.is.hash(c.options)&&(b.is.string(c.options.filterValue)&&(c=c.options.filterValue),b.is.defined(c.value)&&(c=c.value)),b.is.object(c)&&b.is["boolean"](c._isAMomentObject))return c.format(this.formatString);if(b.is.string(c)){if(isNaN(c))return c;c=+c}return b.is.number(c)||b.is.date(c)?moment(c).format(this.formatString):b.is.defined(c)&&null!=c?c+"":""}}),b.columns.register("date",b.DateColumn))}(jQuery,FooTable),function(a,b){b.HTMLColumn=b.Column.extend({construct:function(a,b){this._super(a,b,"html")},parser:function(c){if(b.is.string(c)&&(c=a(a.trim(c))),b.is.element(c)&&(c=a(c)),b.is.jq(c)){var d=c.prop("tagName").toLowerCase();if("td"==d||"th"==d){var e=c.data("value");return b.is.defined(e)?e:c.contents()}return c}return null}}),b.columns.register("html",b.HTMLColumn)}(jQuery,FooTable),function(a,b){b.NumberColumn=b.Column.extend({construct:function(a,c){this._super(a,c,"number"),this.decimalSeparator=b.is.string(c.decimalSeparator)?c.decimalSeparator:".",this.thousandSeparator=b.is.string(c.thousandSeparator)?c.thousandSeparator:",",this.decimalSeparatorRegex=new RegExp(b.str.escapeRegExp(this.decimalSeparator),"g"),this.thousandSeparatorRegex=new RegExp(b.str.escapeRegExp(this.thousandSeparator),"g"),this.cleanRegex=new RegExp("[^0-9"+b.str.escapeRegExp(this.decimalSeparator)+"]","g")},parser:function(c){if(b.is.element(c)||b.is.jq(c)){var d=a(c).data("value");c=b.is.defined(d)?d:a(c).text().replace(this.cleanRegex,"")}return b.is.string(c)&&(c=c.replace(this.thousandSeparatorRegex,"").replace(this.decimalSeparatorRegex,"."),c=parseFloat(c)),b.is.number(c)?c:null},formatter:function(a){if(null==a)return"";var b=(a+"").split(".");return 2==b.length&&b[0].length>3&&(b[0]=b[0].replace(/\B(?=(?:\d{3})+(?!\d))/g,this.thousandSeparator)),b.join(this.decimalSeparator)}}),b.columns.register("number",b.NumberColumn)}(jQuery,FooTable),function(a,b){b.Breakpoint=b.Class.extend({construct:function(a,b){this.name=a,this.width=b}})}(jQuery,FooTable),function(a,b){b.Breakpoints=b.Component.extend({construct:function(a){this._super(a,!0),this.o=a.o,this.current=null,this.array=[],this.cascade=this.o.cascade,this.useParentWidth=this.o.useParentWidth,this.hidden=null,this._classNames="",this.getWidth=b.checkFnValue(this,this.o.getWidth,this.getWidth)},preinit:function(a){var c=this;return this.ft.raise("preinit.ft.breakpoints",[a]).then(function(){c.cascade=b.is["boolean"](a.cascade)?a.cascade:c.cascade,c.o.breakpoints=b.is.hash(a.breakpoints)?a.breakpoints:c.o.breakpoints,c.getWidth=b.checkFnValue(c,a.getWidth,c.getWidth),null==c.o.breakpoints&&(c.o.breakpoints={xs:480,sm:768,md:992,lg:1200});for(var d in c.o.breakpoints)c.o.breakpoints.hasOwnProperty(d)&&(c.array.push(new b.Breakpoint(d,c.o.breakpoints[d])),c._classNames+="breakpoint-"+d+" ");c.array.sort(function(a,b){return b.width-a.width})})},init:function(){var a=this;return this.ft.raise("init.ft.breakpoints").then(function(){a.current=a.get()})},draw:function(){this.ft.$el.removeClass(this._classNames).addClass("breakpoint-"+this.current.name)},calculate:function(){for(var a,c=this,d=null,e=[],f=null,g=c.getWidth(),h=0,i=c.array.length;i>h;h++)a=c.array[h],(!d&&h==i-1||g>=a.width&&(f instanceof b.Breakpoint?gd;d++)if(this.cascade?b.str.containsWord(this.hidden,c[d]):c[d]==this.current.name)return!1;return!0},check:function(){var a=this,c=a.get();c instanceof b.Breakpoint&&c!=a.current&&a.ft.raise("before.ft.breakpoints",[a.current,c]).then(function(){var b=a.current;return a.current=c,a.ft.draw().then(function(){a.ft.raise("after.ft.breakpoints",[a.current,b])})})},get:function(a){return b.is.undef(a)?this.calculate():a instanceof b.Breakpoint?a:b.is.string(a)?b.arr.first(this.array,function(b){return b.name==a}):b.is.number(a)&&a>=0&&af&&(f=a.index)}),f++;for(var g,h,i=0;f>i;i++)g={},b.arr.each(c,function(a){return a.index==i?(g=a,!1):void 0}),h={},b.arr.each(d,function(a){return a.index==i?(h=a,!1):void 0}),e.push(a.extend(!0,{},g,h))}return e}var f,g,h=[],i=[],j=d.ft.$el.find("tr.footable-header, thead > tr:last:has([data-breakpoints]), tbody > tr:first:has([data-breakpoints]), thead > tr:last, tbody > tr:first").first();if(j.length>0){var k=j.parent().is("tbody")&&j.children().length==j.children("td").length;k||(d.$header=j.addClass("footable-header")),j.children("td,th").each(function(b,c){f=a(c),g=f.data(),g.index=b,g.$el=f,g.virtual=k,i.push(g)}),k&&(d.showHeader=!1)}b.is.array(d.o.columns)&&!b.is.emptyArray(d.o.columns)?(b.arr.each(d.o.columns,function(a,b){a.index=b,h.push(a)}),d.parseFinalize(c,e(h,i))):b.is.promise(d.o.columns)?d.o.columns.then(function(a){b.arr.each(a,function(a,b){a.index=b,h.push(a)}),d.parseFinalize(c,e(h,i))},function(a){c.reject(Error("Columns ajax request error: "+a.status+" ("+a.statusText+")"))}):d.parseFinalize(c,e(h,i))})},parseFinalize:function(a,c){var d,e=this,f=[];b.arr.each(c,function(a){(d=b.columns.contains(a.type)?b.columns.make(a.type,e.ft,a):new b.Column(e.ft,a))&&f.push(d)}),b.is.emptyArray(f)?a.reject(Error("No columns supplied.")):(f.sort(function(a,b){return a.index-b.index}),a.resolve(f))},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.columns",[a]).then(function(){return c.parse(a).then(function(d){c.array=d,c.showHeader=b.is["boolean"](a.showHeader)?a.showHeader:c.showHeader})})},init:function(){var a=this;return this.ft.raise("init.ft.columns",[a.array]).then(function(){a.$create()})},destroy:function(){var a=this;this.ft.raise("destroy.ft.columns").then(function(){a._fromHTML||a.$header.remove()})},predraw:function(){var a=this,c=!0;a.visibleColspan=0,a.firstVisibleIndex=0,a.lastVisibleIndex=0,a.hasHidden=!1,b.arr.each(a.array,function(b){b.hidden=!a.ft.breakpoints.visible(b.breakpoints),!b.hidden&&b.visible&&(c&&(a.firstVisibleIndex=b.index,c=!1),a.lastVisibleIndex=b.index,a.visibleColspan++),b.hidden&&(a.hasHidden=!0)}),a.ft.$el.toggleClass("breakpoint",a.hasHidden)},draw:function(){b.arr.each(this.array,function(a){a.$el.css("display",a.hidden||!a.visible?"none":"table-cell")}),!this.showHeader&&b.is.jq(this.$header.parent())&&this.$header.detach()},$create:function(){var c=this;c.$header=b.is.jq(c.$header)?c.$header:a("",{"class":"footable-header" +}),c.$header.children("th,td").detach(),b.arr.each(c.array,function(a){c.$header.append(a.$el)}),c.showHeader&&!b.is.jq(c.$header.parent())&&c.ft.$el.children("thead").append(c.$header)},get:function(a){return a instanceof b.Column?a:b.is.string(a)?b.arr.first(this.array,function(b){return b.name==a}):b.is.number(a)?b.arr.first(this.array,function(b){return b.index==a}):b.is.fn(a)?b.arr.get(this.array,a):null},ensure:function(a){var c=this,d=[];return b.is.array(a)?(b.arr.each(a,function(a){d.push(c.get(a))}),d):d}}),b.components.register("columns",b.Columns,900)}(jQuery,FooTable),function(a){a.Defaults.prototype.columns=[],a.Defaults.prototype.showHeader=!0}(FooTable),function(a,b){b.Rows=b.Component.extend({construct:function(a){this._super(a,!0),this.o=a.o,this.array=[],this.all=[],this.showToggle=a.o.showToggle,this.toggleSelector=a.o.toggleSelector,this.toggleColumn=a.o.toggleColumn,this.emptyString=a.o.empty,this.expandFirst=a.o.expandFirst,this.expandAll=a.o.expandAll,this.$empty=null,this._fromHTML=b.is.emptyArray(a.o.rows)&&!b.is.promise(a.o.rows)},parse:function(){var c=this;return a.Deferred(function(a){var d=c.ft.$el.children("tbody").children("tr");b.is.array(c.o.rows)&&c.o.rows.length>0?c.parseFinalize(a,c.o.rows):b.is.promise(c.o.rows)?c.o.rows.then(function(b){c.parseFinalize(a,b)},function(b){a.reject(Error("Rows ajax request error: "+b.status+" ("+b.statusText+")"))}):b.is.jq(d)?(c.parseFinalize(a,d),d.detach()):c.parseFinalize(a,[])})},parseFinalize:function(c,d){var e=this,f=a.map(d,function(a){return new b.Row(e.ft,e.ft.columns.array,a)});c.resolve(f)},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.rows",[a]).then(function(){return c.parse().then(function(d){c.all=d,c.array=c.all.slice(0),c.showToggle=b.is["boolean"](a.showToggle)?a.showToggle:c.showToggle,c.toggleSelector=b.is.string(a.toggleSelector)?a.toggleSelector:c.toggleSelector,c.toggleColumn=b.is.string(a.toggleColumn)?a.toggleColumn:c.toggleColumn,"first"!=c.toggleColumn&&"last"!=c.toggleColumn&&(c.toggleColumn="first"),c.emptyString=b.is.string(a.empty)?a.empty:c.emptyString,c.expandFirst=b.is["boolean"](a.expandFirst)?a.expandFirst:c.expandFirst,c.expandAll=b.is["boolean"](a.expandAll)?a.expandAll:c.expandAll})})},init:function(){var a=this;return a.ft.raise("init.ft.rows",[a.all]).then(function(){a.$create()})},destroy:function(){var a=this;this.ft.raise("destroy.ft.rows").then(function(){b.arr.each(a.array,function(b){b.predraw(!a._fromHTML)})})},predraw:function(){b.arr.each(this.array,function(a){a.predraw()}),this.array=this.all.slice(0)},$create:function(){this.$empty=a("",{"class":"footable-empty"}).append(a("",{"class":"footable-filtering"}).prependTo(d.ft.$el.children("thead")),d.$cell=a(""),this.ft.$el.append(b)),this.$row.appendTo(b),this.detached=!1}this.$cell.attr("colspan",this.ft.columns.visibleColspan),this._createLinks(),this._setVisible(this.current,this.current>this.previous),this._setNavigation(!0),this.$count.text(this.formattedCount)}},$create:function(){this._createdLinks=0;var b="footable-paging-center";switch(this.position){case"left":b="footable-paging-left";break;case"right":b="footable-paging-right"}this.ft.$el.addClass("footable-paging").addClass(b),this.$cell=a(""),this.ft.$el.append(c)),this.$row=a("",{"class":"footable-paging"}).append(this.$cell).appendTo(c),this.$pagination=a(""),b.ft.$el.append(d)),b.$row=a("",{"class":"footable-editing"}).append(b.$cell).appendTo(d)},$buttonShow:function(){return'"},$buttonHide:function(){return'"},$buttonAdd:function(){return' "},$buttonEdit:function(){return' "},$buttonDelete:function(){return'"},$buttonView:function(){return' "},$rowButtons:function(){return b.is.jq(this._$buttons)?this._$buttons.clone():(this._$buttons=a('
'),this.allowView&&this._$buttons.append(this.$buttonView()),this.allowEdit&&this._$buttons.append(this.$buttonEdit()),this.allowDelete&&this._$buttons.append(this.$buttonDelete()),this._$buttons)},draw:function(){this.$cell.attr("colspan",this.ft.columns.visibleColspan)},_onEditClick:function(c){c.preventDefault();var d=c.data.self,e=a(this).closest("tr").data("__FooTableRow__");e instanceof b.Row&&d.ft.raise("edit.ft.editing",[e]).then(function(){d.callbacks.editRow.call(d.ft,e)})},_onDeleteClick:function(c){c.preventDefault();var d=c.data.self,e=a(this).closest("tr").data("__FooTableRow__");e instanceof b.Row&&d.ft.raise("delete.ft.editing",[e]).then(function(){d.callbacks.deleteRow.call(d.ft,e)})},_onViewClick:function(c){c.preventDefault();var d=c.data.self,e=a(this).closest("tr").data("__FooTableRow__");e instanceof b.Row&&d.ft.raise("view.ft.editing",[e]).then(function(){d.callbacks.viewRow.call(d.ft,e)})},_onAddClick:function(a){a.preventDefault();var b=a.data.self;b.ft.raise("add.ft.editing").then(function(){b.callbacks.addRow.call(b.ft)})},_onShowClick:function(a){a.preventDefault();var b=a.data.self;b.ft.raise("show.ft.editing").then(function(){b.ft.$el.addClass("footable-editing-show"),b.column.visible=!0,b.ft.draw()})},_onHideClick:function(a){a.preventDefault();var b=a.data.self;b.ft.raise("hide.ft.editing").then(function(){b.ft.$el.removeClass("footable-editing-show"),b.column.visible=!1,b.ft.draw()})}}),b.components.register("editing",b.Editing,850)}(jQuery,FooTable),function(a,b){b.EditingColumn=b.Column.extend({construct:function(a,b,c){this._super(a,c,"editing"),this.editing=b},$create:function(){(this.$el=!this.virtual&&b.is.jq(this.$el)?this.$el:a("
') - $target.find('tbody').prepend(no_results); - } - } - }); - }); - } - }); - $('[data-action="filter"]').filterTable(); - $('.container').on('click', '.panel-heading span.filter', function(e){ - var $this = $(this), - $panel = $this.parents('.panel'); - $panel.find('.panel-body').slideToggle("fast"); - if($this.css('display') != 'none') { - $panel.find('.panel-body input').focus(); - } - }); }); diff --git a/data/web/mailbox.php b/data/web/mailbox.php index a12a078f..5f2187c0 100644 --- a/data/web/mailbox.php +++ b/data/web/mailbox.php @@ -10,11 +10,8 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
-

+

- - - @@ -24,93 +21,9 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; ?>
-
- -
-
-
").text(this.emptyString))},draw:function(){var a=this,c=a.ft.$el.children("tbody"),d=!0;a.array.length>0?(a.$empty.detach(),b.arr.each(a.array,function(b){(a.expandFirst&&d||a.expandAll)&&(b.expanded=!0,d=!1),b.draw(c)})):(a.$empty.children("td").attr("colspan",a.ft.columns.visibleColspan),c.append(a.$empty))},load:function(c,d){var e=this,f=a.map(c,function(a){return new b.Row(e.ft,e.ft.columns.array,a)});b.arr.each(this.array,function(a){a.predraw()}),this.all=(b.is["boolean"](d)?d:!1)?this.all.concat(f):f,this.array=this.all.slice(0),this.ft.draw()},expand:function(){b.arr.each(this.array,function(a){a.expand()})},collapse:function(){b.arr.each(this.array,function(a){a.collapse()})}}),b.components.register("rows",b.Rows,800)}(jQuery,FooTable),function(a){a.Defaults.prototype.rows=[],a.Defaults.prototype.empty="No results",a.Defaults.prototype.showToggle=!0,a.Defaults.prototype.toggleSelector="tr,td,.footable-toggle",a.Defaults.prototype.toggleColumn="first",a.Defaults.prototype.expandFirst=!1,a.Defaults.prototype.expandAll=!1}(FooTable),function(a){a.Table.prototype.loadRows=function(a,b){this.rows.load(a,b)}}(FooTable),function(a){a.Filter=a.Class.extend({construct:function(b,c,d,e,f,g,h){this.name=b,this.space=!a.is.string(e)||"OR"!=e&&"AND"!=e?"AND":e,this.connectors=a.is["boolean"](f)?f:!0,this.ignoreCase=a.is["boolean"](g)?g:!0,this.hidden=a.is["boolean"](h)?h:!1,this.query=c instanceof a.Query?c:new a.Query(c,this.space,this.connectors,this.ignoreCase),this.columns=d},match:function(b){return a.is.string(b)?(a.is.string(this.query)&&(this.query=new a.Query(this.query,this.space,this.connectors,this.ignoreCase)),this.query instanceof a.Query?this.query.match(b):!1):!1},matchRow:function(b){var c=this,d=a.arr.map(b.cells,function(b){return a.arr.contains(c.columns,b.column)?b.filterValue:null}).join(" ");return c.match(d)}})}(FooTable),function(a,b){b.Filtering=b.Component.extend({construct:function(a){this._super(a,a.o.filtering.enabled),this.filters=a.o.filtering.filters,this.delay=a.o.filtering.delay,this.min=a.o.filtering.min,this.space=a.o.filtering.space,this.connectors=a.o.filtering.connectors,this.ignoreCase=a.o.filtering.ignoreCase,this.exactMatch=a.o.filtering.exactMatch,this.placeholder=a.o.filtering.placeholder,this.dropdownTitle=a.o.filtering.dropdownTitle,this.position=a.o.filtering.position,this.$row=null,this.$cell=null,this.$dropdown=null,this.$input=null,this.$button=null,this._filterTimeout=null,this._exactRegExp=/^"(.*?)"$/},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.filtering").then(function(){c.ft.$el.hasClass("footable-filtering")&&(c.enabled=!0),c.enabled=b.is["boolean"](a.filtering)?a.filtering:c.enabled,c.enabled&&(c.space=b.is.string(a.filterSpace)?a.filterSpace:c.space,c.min=b.is.number(a.filterMin)?a.filterMin:c.min,c.connectors=b.is["boolean"](a.filterConnectors)?a.filterConnectors:c.connectors,c.ignoreCase=b.is["boolean"](a.filterIgnoreCase)?a.filterIgnoreCase:c.ignoreCase,c.exactMatch=b.is["boolean"](a.filterExactMatch)?a.filterExactMatch:c.exactMatch,c.delay=b.is.number(a.filterDelay)?a.filterDelay:c.delay,c.placeholder=b.is.string(a.filterPlaceholder)?a.filterPlaceholder:c.placeholder,c.dropdownTitle=b.is.string(a.filterDropdownTitle)?a.filterDropdownTitle:c.dropdownTitle,c.filters=b.is.array(a.filterFilters)?c.ensure(a.filterFilters):c.ensure(c.filters),c.ft.$el.hasClass("footable-filtering-left")&&(c.position="left"),c.ft.$el.hasClass("footable-filtering-center")&&(c.position="center"),c.ft.$el.hasClass("footable-filtering-right")&&(c.position="right"),c.position=b.is.string(a.filterPosition)?a.filterPosition:c.position)},function(){c.enabled=!1})},init:function(){var a=this;return a.ft.raise("init.ft.filtering").then(function(){a.$create()},function(){a.enabled=!1})},destroy:function(){var a=this;return a.ft.raise("destroy.ft.filtering").then(function(){a.ft.$el.removeClass("footable-filtering").find("thead > tr.footable-filtering").remove()})},$create:function(){var c,d=this,e=a("
",{"class":"form-group footable-filtering-search"}).append(a("
").attr("colspan",d.ft.columns.visibleColspan).appendTo(d.$row),d.$form=a("
",{"class":"form-inline"}).append(e).appendTo(d.$cell),d.$input=a("",{type:"text","class":"form-control",placeholder:d.placeholder}),d.$button=a("
").attr("colspan",this.ft.columns.visibleColspan);var c=this.ft.$el.children("tfoot");0==c.length&&(c=a("
").attr("colspan",b.ft.columns.visibleColspan).append(b.$buttonShow()),b.allowAdd&&b.$cell.append(b.$buttonAdd()),b.$cell.append(b.$buttonHide()),b.alwaysShow&&b.ft.$el.addClass("footable-editing-always-show"),b.allowAdd||b.ft.$el.addClass("footable-editing-no-add"),b.allowEdit||b.ft.$el.addClass("footable-editing-no-edit"),b.allowDelete||b.ft.$el.addClass("footable-editing-no-delete"),b.allowView||b.ft.$el.addClass("footable-editing-no-view");var d=b.ft.$el.children("tfoot");0==d.length&&(d=a("
",{"class":"footable-editing"})).html(this.title)},parser:function(c){if(b.is.string(c)&&(c=a(a.trim(c))),b.is.element(c)&&(c=a(c)),b.is.jq(c)){var d=c.prop("tagName").toLowerCase();return"td"==d||"th"==d?c.data("value")||c.contents():c}return null},createCell:function(c){var d=this.editing.$rowButtons(),e=a("").append(d);return b.is.jq(c.$el)&&(0===this.index?e.prependTo(c.$el):e.insertAfter(c.$el.children().eq(this.index-1))),new b.Cell(this.ft,c,this,e||e.html())}}),b.columns.register("editing",b.EditingColumn)}(jQuery,FooTable),function(a,b){b.Defaults.prototype.editing={enabled:!1,pageToNew:!0,position:"right",alwaysShow:!1,addRow:function(){},editRow:function(a){},deleteRow:function(a){},viewRow:function(a){},showText:' Edit rows',hideText:"Cancel",addText:"New row",editText:'',deleteText:'',viewText:'',allowAdd:!0,allowEdit:!0,allowDelete:!0,allowView:!1,column:{classes:"footable-editing",name:"editing",title:"",filterable:!1,sortable:!1}}}(jQuery,FooTable),function(a,b){b.is.defined(b.Paging)&&(b.Paging.prototype.unpaged=[],b.Paging.extend("predraw",function(){this.unpaged=this.ft.rows.array.slice(0),this._super()}))}(jQuery,FooTable),function(a,b){b.Row.prototype.add=function(c){c=b.is["boolean"](c)?c:!0;var d=this;return a.Deferred(function(a){var b=d.ft.rows.all.push(d)-1;return c?d.ft.draw().then(function(){a.resolve(b)}):void a.resolve(b)})},b.Row.prototype["delete"]=function(c){c=b.is["boolean"](c)?c:!0;var d=this;return a.Deferred(function(a){var e=d.ft.rows.all.indexOf(d);return b.is.number(e)&&e>=0&&e=0&&e>b&&(f=this.ft.rows.all[b]),f instanceof FooTable.Row&&a.is.hash(c)&&f.val(c,d)},a.Rows.prototype["delete"]=function(b,c){var d=this.ft.rows.all.length,e=b;a.is.number(b)&&b>=0&&d>b&&(e=this.ft.rows.all[b]),e instanceof FooTable.Row&&e["delete"](c)}}(FooTable),function(a,b){var c=0,d=function(a){var b,c,d=2166136261;for(b=0,c=a.length;c>b;b++)d^=a.charCodeAt(b),d+=(d<<1)+(d<<4)+(d<<7)+(d<<8)+(d<<24);return d>>>0}(location.origin+location.pathname);b.State=b.Component.extend({construct:function(a){this._super(a,a.o.state.enabled),this._key="1",this.key=this._key+(b.is.string(a.o.state.key)?a.o.state.key:this._uid()),this.filtering=b.is["boolean"](a.o.state.filtering)?a.o.state.filtering:!0,this.paging=b.is["boolean"](a.o.state.paging)?a.o.state.paging:!0,this.sorting=b.is["boolean"](a.o.state.sorting)?a.o.state.sorting:!0},preinit:function(a){var c=this;this.ft.raise("preinit.ft.state",[a]).then(function(){c.enabled=b.is["boolean"](a.state)?a.state:c.enabled,c.enabled&&(c.key=c._key+(b.is.string(a.stateKey)?a.stateKey:c.key),c.filtering=b.is["boolean"](a.stateFiltering)?a.stateFiltering:c.filtering,c.paging=b.is["boolean"](a.statePaging)?a.statePaging:c.paging,c.sorting=b.is["boolean"](a.stateSorting)?a.stateSorting:c.sorting)},function(){c.enabled=!1})},get:function(a){return JSON.parse(localStorage.getItem(this.key+":"+a))},set:function(a,b){localStorage.setItem(this.key+":"+a,JSON.stringify(b))},remove:function(a){localStorage.removeItem(this.key+":"+a)},read:function(){this.ft.execute(!1,!0,"readState")},write:function(){this.ft.execute(!1,!0,"writeState")},clear:function(){this.ft.execute(!1,!0,"clearState")},_uid:function(){var a=this.ft.$el.attr("id");return d+"_"+(b.is.string(a)?a:++c)}}),b.components.register("state",b.State,700)}(jQuery,FooTable),function(a){a.Component.prototype.readState=function(){},a.Component.prototype.writeState=function(){},a.Component.prototype.clearState=function(){}}(FooTable),function(a){a.Defaults.prototype.state={enabled:!1,filtering:!0,paging:!0,sorting:!0,key:null}}(FooTable),function(a){a.Filtering&&(a.Filtering.prototype.readState=function(){if(this.ft.state.filtering){var b=this.ft.state.get("filtering");a.is.hash(b)&&!a.is.emptyArray(b.filters)&&(this.filters=this.ensure(b.filters))}},a.Filtering.prototype.writeState=function(){if(this.ft.state.filtering){var b=a.arr.map(this.filters,function(b){return{name:b.name,query:b.query instanceof a.Query?b.query.val():b.query,columns:a.arr.map(b.columns,function(a){return a.name}),hidden:b.hidden,space:b.space,connectors:b.connectors,ignoreCase:b.ignoreCase}});this.ft.state.set("filtering",{filters:b})}},a.Filtering.prototype.clearState=function(){this.ft.state.filtering&&this.ft.state.remove("filtering")})}(FooTable),function(a){a.Paging&&(a.Paging.prototype.readState=function(){if(this.ft.state.paging){var b=this.ft.state.get("paging");a.is.hash(b)&&(this.current=b.current,this.size=b.size)}},a.Paging.prototype.writeState=function(){this.ft.state.paging&&this.ft.state.set("paging",{current:this.current,size:this.size})},a.Paging.prototype.clearState=function(){this.ft.state.paging&&this.ft.state.remove("paging")})}(FooTable),function(a){a.Sorting&&(a.Sorting.prototype.readState=function(){if(this.ft.state.sorting){var b=this.ft.state.get("sorting");if(a.is.hash(b)){var c=this.ft.columns.get(b.column);c instanceof a.Column&&(this.column=c,this.column.direction=b.direction)}}},a.Sorting.prototype.writeState=function(){this.ft.state.sorting&&this.column instanceof a.Column&&this.ft.state.set("sorting",{column:this.column.name,direction:this.column.direction})},a.Sorting.prototype.clearState=function(){this.ft.state.sorting&&this.ft.state.remove("sorting")})}(FooTable),function(a){a.Table.extend("_construct",function(a){this.state=this.use(FooTable.State),this._super(a)}),a.Table.extend("_preinit",function(){var a=this;return a._super().then(function(){a.state.enabled&&a.state.read()})}),a.Table.extend("draw",function(){var a=this;return a._super().then(function(){a.state.enabled&&a.state.write()})})}(FooTable); \ No newline at end of file diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index eedd51cb..3a12b610 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -1,54 +1,112 @@ $(document).ready(function() { // Show element counter for tables $('[data-toggle="tooltip"]').tooltip(); - var rowCountDomainAlias = $('#domainaliastable >tbody >#data').length; - var rowCountDomain = $('#domaintable >tbody >#data').length; - var rowCountMailbox = $('#mailboxtable >tbody >#data').length; - var rowCountAlias = $('#aliastable >tbody >#data').length; - var rowCountResource = $('#resourcetable >tbody >#data').length; - $("#numRowsDomainAlias").text(rowCountDomainAlias); - $("#numRowsDomain").text(rowCountDomain); - $("#numRowsMailbox").text(rowCountMailbox); - $("#numRowsAlias").text(rowCountAlias); - $("#numRowsResource").text(rowCountResource); + function humanFileSize(bytes) { + if(Math.abs(bytes) < 1024) { + return bytes + ' B'; + } + var units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB']; + var u = -1; + do { + bytes /= 1024; + ++u; + } while(Math.abs(bytes) >= 1024 && u < units.length - 1); + return bytes.toFixed(1)+' '+units[u]; + } + + $.ajax({ + dataType: 'json', + url: '/json_api.php?action=domain_table_data', + jsonp: false, + error: function () { + alert('Cannot receive history'); + }, + success: function (data) { + $.each(data, function (i, item) { + item.aliases = item.aliases_in_domain + " / " + item.max_num_aliases_for_domain; + item.mailboxes = item.mboxes_in_domain + " / " + item.max_num_mboxes_for_domain; + item.quota = humanFileSize(item.quota_used_in_domain) + " / " + humanFileSize(item.max_quota_for_domain); + item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox); + item.action = '
' + + ' Edit' + + ' Remove' + + '
'; + }); + $('#domain_table').footable({ + "columns": [ + {"sorted": true,"name":"domain_name","title":lang_domain}, + {"name":"aliases","title":lang_aliases,"breakpoints":"xs sm"}, + {"name":"mailboxes","title":lang_mailboxes}, + {"name":"quota","title":lang_domain_quota}, + {"name":"max_quota_for_mbox","title":lang_mailbox_quota}, + {"name":"backupmx","title":lang_backup_mx,"breakpoints":"xs sm"}, + {"name":"active","title":lang_active,"breakpoints":"xs sm"}, + {"name":"action","type":"html","title":lang_action,"breakpoints":"xs sm"} + ], + "rows": data, + "paging": { + "enabled": true, + "limit": 5, + "size": 25 + }, + "filtering": { + "enabled": true, + "position": "left" + }, + "sorting": { + "enabled": true + } + }); + } + }); + + $.ajax({ + dataType: 'json', + url: '/json_api.php?action=mailbox_table_data', + jsonp: false, + error: function () { + alert('Cannot receive history'); + }, + 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); + item.action = '
' + + ' Edit' + + ' Remove' + + '
'; + item.in_use = '
' + + '
' + item.percent_in_use + '%' + '
'; + + }); + $('#mailbox_table').footable({ + "columns": [ + {"sorted": true,"name":"username","title":lang_username}, + {"name":"name","title":lang_fname,"breakpoints":"xs sm"}, + {"name":"domain","title":lang_domain}, + {"name":"quota","title":lang_domain_quota}, + {"name":"spam_aliases","title":lang_spam_aliases}, + {"name":"in_use","type":"html","title":lang_in_use}, + {"name":"messages","title":lang_msg_num,"breakpoints":"xs sm"}, + {"name":"active","title":lang_active,"breakpoints":"xs sm"}, + {"name":"action","type":"html","title":lang_action,"breakpoints":"xs sm"} + ], + "rows": data, + "paging": { + "enabled": true, + "limit": 5, + "size": 25 + }, + "filtering": { + "enabled": true, + "position": "left" + }, + "sorting": { + "enabled": true + } + }); + } + }); - // Filter table function - $.fn.extend({ - filterTable: function(){ - return this.each(function(){ - $(this).on('keyup', function(e){ - var $this = $(this), - search = $this.val().toLowerCase(), - target = $this.attr('data-filters'), - $target = $(target), - $rows = $target.find('tbody #data'); - $target.find('tbody .filterTable_no_results').remove(); - if(search == '') { - $target.find('tbody #no-data').show(); - $rows.show(); - } else { - $target.find('tbody #no-data').hide(); - $rows.each(function(){ - var $this = $(this); - $this.text().toLowerCase().indexOf(search) === -1 ? $this.hide() : $this.show(); - }) - if($target.find('tbody #data:visible').size() === 0) { - var col_count = $target.find('#data').first().find('td').size(); - var no_results = $('
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / / -
- - -
-
-
- -
-
- -
-
+
+
+
@@ -118,91 +31,14 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' . htmlspecialchars($mailboxdata['username']) . '';?> / -
-
- % -
-
-
-
- - - - Login - -
-
- -
-
+
+
+
@@ -210,75 +46,15 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
-

+

- - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
- -
+
+
+
@@ -287,71 +63,15 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
-

+

- - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
- -
+
+
+
@@ -361,87 +81,35 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
-

+

- - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Catch-all ' . htmlspecialchars($aliasdata['address']) : htmlspecialchars($aliasdata['address']); ?> - - - -
- - -
-
- -
+
+
+
- + + Date: Tue, 21 Mar 2017 10:02:23 +0100 Subject: [PATCH 19/55] Add footable --- data/conf/nginx/server_name.active | 2 +- data/web/inc/functions.inc.php | 2 +- data/web/inc/header.inc.php | 1 + data/web/js/mailbox.js | 176 +++++++++++++++++++++++++---- data/web/json_api.php | 105 ++++++++++++++++- data/web/mailbox.php | 57 +++++----- docker-compose.yml | 3 + 7 files changed, 286 insertions(+), 60 deletions(-) diff --git a/data/conf/nginx/server_name.active b/data/conf/nginx/server_name.active index ce429200..7309d8f6 100644 --- a/data/conf/nginx/server_name.active +++ b/data/conf/nginx/server_name.active @@ -1 +1 @@ -server_name logs.servercow.de autodiscover.* autoconfig.*; +server_name mail.develcow.de autodiscover.* autoconfig.*; diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 9c2bdbc1..c37e39ab 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -4422,7 +4422,7 @@ function mailbox_get_resource_details($resource) { $resourcedata['name'] = $row['username']; $resourcedata['kind'] = $row['kind']; $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['active'] = $row['active']; $resourcedata['active_int'] = $row['active_int']; diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php index a128df88..ceacaf8e 100644 --- a/data/web/inc/header.inc.php +++ b/data/web/inc/header.inc.php @@ -15,6 +15,7 @@ + diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index 3a12b610..55e21c32 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -28,22 +28,23 @@ $(document).ready(function() { item.quota = humanFileSize(item.quota_used_in_domain) + " / " + humanFileSize(item.max_quota_for_domain); item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox); item.action = ''; }); $('#domain_table').footable({ "columns": [ - {"sorted": true,"name":"domain_name","title":lang_domain}, - {"name":"aliases","title":lang_aliases,"breakpoints":"xs sm"}, - {"name":"mailboxes","title":lang_mailboxes}, - {"name":"quota","title":lang_domain_quota}, - {"name":"max_quota_for_mbox","title":lang_mailbox_quota}, - {"name":"backupmx","title":lang_backup_mx,"breakpoints":"xs sm"}, - {"name":"active","title":lang_active,"breakpoints":"xs sm"}, - {"name":"action","type":"html","title":lang_action,"breakpoints":"xs sm"} + {"sorted": true,"name":"domain_name","title":lang.domain,"style":{"width":"250px"}}, + {"name":"aliases","title":lang.aliases,"breakpoints":"xs sm"}, + {"name":"mailboxes","title":lang.mailboxes}, + {"name":"quota","title":lang.domain_quota}, + {"name":"max_quota_for_mbox","title":lang.mailbox_quota}, + {"name":"backupmx","title":lang.backup_mx,"breakpoints":"xs sm"}, + {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, + {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} ], "rows": data, + "empty": lang.empty, "paging": { "enabled": true, "limit": 5, @@ -51,7 +52,8 @@ $(document).ready(function() { }, "filtering": { "enabled": true, - "position": "left" + "position": "left", + "placeholder": lang.search }, "sorting": { "enabled": true @@ -72,8 +74,8 @@ $(document).ready(function() { item.quota = humanFileSize(item.quota_used) + " / " + humanFileSize(item.quota); item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox); item.action = ''; item.in_use = '
' + '
' + lang.edit + '' + + ' ' + lang.remove + '' + + '
'; + }); + $('#resources_table').footable({ + "columns": [ + {"sorted": true,"name":"description","title":lang.description,"style":{"width":"250px"}}, + {"name":"kind","title":lang.kind,"breakpoints":"xs sm"}, + {"name":"domain","title":lang.domain}, + {"name":"multiple_bookings","title":lang.multiple_bookings}, + {"name":"domain","title":lang.domain}, + {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, + {"name":"action","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": 25 + }, + "filtering": { + "enabled": true, + "position": "left", + "placeholder": lang.search + }, + "sorting": { + "enabled": true + } + }); + } + }); + + $.ajax({ + dataType: 'json', + url: '/json_api.php?action=domain_alias_table_data', + jsonp: false, + error: function () { + alert('Cannot receive history'); + }, + success: function (data) { + $.each(data, function (i, item) { + item.action = ''; + }); + $('#aliasdomain_table').footable({ + "columns": [ + {"sorted": true,"name":"alias_domain","title":lang.alias,"style":{"width":"250px"}}, + {"name":"target_domain","title":lang.target_domain,"breakpoints":"xs sm"}, + {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, + {"name":"action","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": 25 + }, + "filtering": { + "enabled": true, + "position": "left", + "placeholder": lang.search + }, + "sorting": { + "enabled": true + } + }); + } + }); + + $.ajax({ + dataType: 'json', + url: '/json_api.php?action=alias_table_data', + jsonp: false, + error: function () { + alert('Cannot receive history'); + }, + success: function (data) { + $.each(data, function (i, item) { + if (item.is_catch_all == 1) { + item.address = '
Catch-All
' + item.address; + } + item.action = ''; + }); + $('#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}, + {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, + {"name":"action","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": 5 + }, + "filtering": { + "enabled": true, + "position": "left", + "placeholder": lang.search + }, + "sorting": { + "enabled": true + } + }); + } + }); }); diff --git a/data/web/json_api.php b/data/web/json_api.php index d404b0bd..89ed85a0 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -2,13 +2,104 @@ require_once 'inc/prerequisites.inc.php'; error_reporting(E_ALL); if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) { - if ($_GET['action'] && $_GET['object']) { + if (isset($_GET['action'])) { $action = $_GET['action']; - $object = $_GET['object']; 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); + } + } + 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); + } + } + 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); + } + } + 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); + } + } + 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); + } + } + break; case "get_mailbox_details": + if (!isset($_GET['object'])) { return false; } + $object = $_GET['object']; $data = mailbox_get_mailbox_details($object); - if (!$data || empty($data)) { + if (!isset($data) || empty($data)) { echo '{}'; } else { @@ -16,8 +107,10 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u } break; case "get_domain_details": + if (!isset($_GET['object'])) { return false; } + $object = $_GET['object']; $data = mailbox_get_domain_details($object); - if (!$data || empty($data)) { + if (!isset($data) || empty($data)) { echo '{}'; } else { @@ -25,6 +118,8 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u } break; case "get_u2f_reg_challenge": + if (!isset($_GET['object'])) { return false; } + $object = $_GET['object']; if ( ($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") && @@ -40,6 +135,8 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u } break; 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) { $reqs = json_encode($u2f->getAuthenticateData(get_u2f_registrations($object))); $_SESSION['authReq'] = $reqs; diff --git a/data/web/mailbox.php b/data/web/mailbox.php index 5f2187c0..8873859a 100644 --- a/data/web/mailbox.php +++ b/data/web/mailbox.php @@ -5,6 +5,15 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm require_once "inc/header.inc.php"; $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; ?> +
@@ -36,9 +45,9 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
-
-
-
+
+
+
@@ -51,11 +60,9 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; -
-
-
-
-
+
+
+
@@ -68,11 +75,9 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; -
-
-
-
-
+
+
+
@@ -86,28 +91,18 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; -
-
-
-
-
+
+
+
- diff --git a/docker-compose.yml b/docker-compose.yml index 76ca1b6b..47528305 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -261,6 +261,9 @@ networks: volumes: vmail-vol-1: + driver: nfs + driver_opts: + share: io.servercow.de:/nfs_test mysql-vol-1: dkim-vol-1: redis-vol-1: From 5512cff3d68913e9ca4c4f99b11719c8b7359aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20P?= Date: Tue, 21 Mar 2017 10:03:49 +0100 Subject: [PATCH 20/55] Add ignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9e8412e2..6ccd199d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ data/conf/dovecot/dovecot-master.passwd mailcow.conf mailcow.conf_backup data/conf/nginx/listen*active -data/conf/nginx/server_name*active +data/conf/nginx/server_name.active data/conf/postfix/sql data/conf/dovecot/sql data/web/inc/vars.local.inc.php From d8cf921e357a8058a5ea79e51467451517658e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20P?= Date: Tue, 21 Mar 2017 10:04:26 +0100 Subject: [PATCH 21/55] Add ignore --- data/conf/nginx/server_name.active | 1 - 1 file changed, 1 deletion(-) delete mode 100644 data/conf/nginx/server_name.active diff --git a/data/conf/nginx/server_name.active b/data/conf/nginx/server_name.active deleted file mode 100644 index 7309d8f6..00000000 --- a/data/conf/nginx/server_name.active +++ /dev/null @@ -1 +0,0 @@ -server_name mail.develcow.de autodiscover.* autoconfig.*; From 8d1784a1c4d071287d086e70c5c4c509e4c44778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20P?= Date: Tue, 21 Mar 2017 10:05:26 +0100 Subject: [PATCH 22/55] Reset ratelimit --- data/conf/rspamd/local.d/ratelimit.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/conf/rspamd/local.d/ratelimit.conf b/data/conf/rspamd/local.d/ratelimit.conf index 36234214..74e22489 100644 --- a/data/conf/rspamd/local.d/ratelimit.conf +++ b/data/conf/rspamd/local.d/ratelimit.conf @@ -1,5 +1,5 @@ rates { - to = [1100, 0.033333333]; + to = [100, 0.033333333]; to_ip = [30, 0.025]; to_ip_from = [20, 0.01666666667]; bounce_to = [10, 0.000555556]; From aae1b9c9f21ed32650601bfb01ffe8be8ce03f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20P?= Date: Tue, 21 Mar 2017 10:06:00 +0100 Subject: [PATCH 23/55] Reset docker-compose --- docker-compose.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 47528305..76ca1b6b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -261,9 +261,6 @@ networks: volumes: vmail-vol-1: - driver: nfs - driver_opts: - share: io.servercow.de:/nfs_test mysql-vol-1: dkim-vol-1: redis-vol-1: From 736fb91f4e6168073a32d8890d0a45cf77d3b623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20P?= Date: Tue, 21 Mar 2017 10:38:16 +0100 Subject: [PATCH 24/55] Minor fixes for footable --- data/web/js/mailbox.js | 63 +++++++++++++++++++++--------------------- data/web/mailbox.php | 6 ++++ 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index 55e21c32..d38cbc63 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -19,7 +19,7 @@ $(document).ready(function() { url: '/json_api.php?action=domain_table_data', jsonp: false, error: function () { - alert('Cannot receive history'); + alert('Cannot draw domain table'); }, success: function (data) { $.each(data, function (i, item) { @@ -38,10 +38,10 @@ $(document).ready(function() { {"name":"aliases","title":lang.aliases,"breakpoints":"xs sm"}, {"name":"mailboxes","title":lang.mailboxes}, {"name":"quota","title":lang.domain_quota}, - {"name":"max_quota_for_mbox","title":lang.mailbox_quota}, - {"name":"backupmx","title":lang.backup_mx,"breakpoints":"xs sm"}, - {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, - {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + {"name":"max_quota_for_mbox","title":lang.mailbox_quota,"breakpoints":"xs sm"}, + {"name":"backupmx","filterable": false,"style":{"maxWidth":"120px","width":"120px"},"title":lang.backup_mx,"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"} ], "rows": data, "empty": lang.empty, @@ -53,7 +53,7 @@ $(document).ready(function() { "filtering": { "enabled": true, "position": "left", - "placeholder": lang.search + "placeholder": lang.filter_table }, "sorting": { "enabled": true @@ -67,7 +67,7 @@ $(document).ready(function() { url: '/json_api.php?action=mailbox_table_data', jsonp: false, error: function () { - alert('Cannot receive history'); + alert('Cannot draw mailbox table'); }, success: function (data) { $.each(data, function (i, item) { @@ -86,13 +86,13 @@ $(document).ready(function() { "columns": [ {"sorted": true,"name":"username","title":lang.username,"style":{"width":"250px"}}, {"name":"name","title":lang.fname,"breakpoints":"xs sm"}, - {"name":"domain","title":lang.domain}, + {"name":"domain","title":lang.domain,"breakpoints":"xs sm"}, {"name":"quota","title":lang.domain_quota}, - {"name":"spam_aliases","title":lang.spam_aliases}, - {"name":"in_use","type":"html","title":lang.in_use}, - {"name":"messages","title":lang.msg_num,"breakpoints":"xs sm"}, - {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, - {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + {"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":{"maxWidth":"120px","width":"120px"},"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","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} ], "empty": lang.empty, "rows": data, @@ -104,7 +104,7 @@ $(document).ready(function() { "filtering": { "enabled": true, "position": "left", - "placeholder": lang.search + "placeholder": lang.filter_table }, "sorting": { "enabled": true @@ -118,7 +118,7 @@ $(document).ready(function() { url: '/json_api.php?action=resource_table_data', jsonp: false, error: function () { - alert('Cannot receive history'); + alert('Cannot draw resource table'); }, success: function (data) { $.each(data, function (i, item) { @@ -130,12 +130,11 @@ $(document).ready(function() { $('#resources_table').footable({ "columns": [ {"sorted": true,"name":"description","title":lang.description,"style":{"width":"250px"}}, - {"name":"kind","title":lang.kind,"breakpoints":"xs sm"}, - {"name":"domain","title":lang.domain}, - {"name":"multiple_bookings","title":lang.multiple_bookings}, - {"name":"domain","title":lang.domain}, - {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, - {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + {"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, @@ -147,7 +146,7 @@ $(document).ready(function() { "filtering": { "enabled": true, "position": "left", - "placeholder": lang.search + "placeholder": lang.filter_table }, "sorting": { "enabled": true @@ -161,7 +160,7 @@ $(document).ready(function() { url: '/json_api.php?action=domain_alias_table_data', jsonp: false, error: function () { - alert('Cannot receive history'); + alert('Cannot draw alias domain table'); }, success: function (data) { $.each(data, function (i, item) { @@ -173,9 +172,9 @@ $(document).ready(function() { $('#aliasdomain_table').footable({ "columns": [ {"sorted": true,"name":"alias_domain","title":lang.alias,"style":{"width":"250px"}}, - {"name":"target_domain","title":lang.target_domain,"breakpoints":"xs sm"}, - {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, - {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + {"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, @@ -187,7 +186,7 @@ $(document).ready(function() { "filtering": { "enabled": true, "position": "left", - "placeholder": lang.search + "placeholder": lang.filter_table }, "sorting": { "enabled": true @@ -201,7 +200,7 @@ $(document).ready(function() { url: '/json_api.php?action=alias_table_data', jsonp: false, error: function () { - alert('Cannot receive history'); + alert('Cannot draw alias table'); }, success: function (data) { $.each(data, function (i, item) { @@ -217,9 +216,9 @@ $(document).ready(function() { "columns": [ {"sorted": true,"name":"address","title":lang.alias,"style":{"width":"250px"}}, {"name":"goto","title":lang.target_address}, - {"name":"domain","title":lang.domain}, - {"name":"active","style":{"maxWidth":"50px","width":"70px"},"title":lang.active}, - {"name":"action","sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + {"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, @@ -231,7 +230,7 @@ $(document).ready(function() { "filtering": { "enabled": true, "position": "left", - "placeholder": lang.search + "placeholder": lang.filter_table }, "sorting": { "enabled": true diff --git a/data/web/mailbox.php b/data/web/mailbox.php index 8873859a..c3234e62 100644 --- a/data/web/mailbox.php +++ b/data/web/mailbox.php @@ -13,6 +13,12 @@ table.footable>tbody>tr.footable-empty>td { .pagination a { text-decoration: none !important; } +.panel panel-default { + overflow: visible !important; +} +.table-responsive { + overflow: visible !important; +}
From f3847592823028f30fca008341b8690df71ee956 Mon Sep 17 00:00:00 2001 From: andryyy Date: Tue, 21 Mar 2017 11:20:04 +0100 Subject: [PATCH 25/55] Add all defaults to ratelimit module config in Rspamd --- data/conf/rspamd/local.d/ratelimit.conf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/data/conf/rspamd/local.d/ratelimit.conf b/data/conf/rspamd/local.d/ratelimit.conf index 74e22489..eca11a12 100644 --- a/data/conf/rspamd/local.d/ratelimit.conf +++ b/data/conf/rspamd/local.d/ratelimit.conf @@ -1,8 +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; From 4518f6f896c9d5dfdf69f523e0692eac84af3274 Mon Sep 17 00:00:00 2001 From: andryyy Date: Tue, 21 Mar 2017 12:22:13 +0100 Subject: [PATCH 26/55] Add and remove mailcow apps to login screen, fixes #120 --- data/web/inc/vars.inc.php | 14 + data/web/index.php | 6 +- docs/u_and_e.md | 709 +++++++++++++++++++------------------- 3 files changed, 373 insertions(+), 356 deletions(-) diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index db3f4219..ed5501d3 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -37,4 +37,18 @@ $DEFAULT_THEME = "lumen"; // Password complexity as regular expression $PASSWD_REGEP = '.{4,}'; + +// mailcow Apps - buttons on login screen +$MAILCOW_APPS = array( + array( + 'name' => 'SOGo', + 'link' => '/SOGo/' + ), + // array( + // 'name' => 'Roundcube', + // 'link' => '/rc/' + // ), +); + + ?> diff --git a/data/web/index.php b/data/web/index.php index e34b4da5..89b9917c 100644 --- a/data/web/index.php +++ b/data/web/index.php @@ -63,7 +63,11 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; endif; ?> mailcow Apps - + ' . $app['name'] . ' '; + } + ?>
diff --git a/docs/u_and_e.md b/docs/u_and_e.md index bcb47517..312dd34f 100644 --- a/docs/u_and_e.md +++ b/docs/u_and_e.md @@ -1,58 +1,57 @@ -## Anonymize headers +## Add mailcow app buttons + +Open `data/web/inc/vars.local.inc.php` and add your apps to an array. Default configuration: -Save as `data/conf/postfix/mailcow_anonymize_headers.pcre`: ``` -/^\s*Received:[^\)]+\)\s+\(Authenticated sender:(.+)/ - REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1 -/^\s*User-Agent/ IGNORE -/^\s*X-Enigmail/ IGNORE -/^\s*X-Mailer/ IGNORE -/^\s*X-Originating-IP/ IGNORE -/^\s*X-Forward/ IGNORE +$MAILCOW_APPS = array( + array( + 'name' => 'SOGo', + 'link' => '/SOGo/' + ), + // array( + // 'name' => 'Roundcube', + // 'link' => '/rc/' + // ), +); ``` -Add this to `data/conf/postfix/main.cf`: +## Backup and restore maildir (simple tar file) + +### Backup + +This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory: ``` -smtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre +cd /path/to/mailcow-dockerized +source mailcow.conf +DATE=$(date +"%Y%m%d_%H%M%S") +docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail +``` + +You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to. +Set the filename `backup_vmail.tar.gz` to any custom name, but leave the path as it is. Example: `[...] tar cvfz /backup/my_own_filename_.tar.gz` + +### Restore +``` +cd /path/to/mailcow-dockerized +source mailcow.conf +DATE=$(date +"%Y%m%d_%H%M%S") +docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz ``` -## Backup and restore maildir (simple tar file) - -### Backup - -This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory: -``` -cd /path/to/mailcow-dockerized -source mailcow.conf -DATE=$(date +"%Y%m%d_%H%M%S") -docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail -``` - -You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to. -Set the filename `backup_vmail.tar.gz` to any custom name, but leave the path as it is. Example: `[...] tar cvfz /backup/my_own_filename_.tar.gz` - -### Restore -``` -cd /path/to/mailcow-dockerized -source mailcow.conf -DATE=$(date +"%Y%m%d_%H%M%S") -docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz -``` - ## Docker Compose Bash completion -For the tab-tab... :-) - -``` -curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose -``` -## Black and Whitelist - -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. - +For the tab-tab... :-) + +``` +curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose +``` +## Black and Whitelist + +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. + ## Change default language Change `data/conf/sogo/sogo.conf` and replace "English" by your preferred language. @@ -62,218 +61,218 @@ Create a file `data/web/inc/vars.local.inc.php` and add "DEFAULT_LANG" with eith array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) -); -$config['enable_installer'] = false; -$config['smtp_conn_options'] = array( -'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) -); -``` - -Point your browser to `https://myserver/rc/installer` and follow the instructions. -Initialize the database and leave the installer. - -**Delete the directory `data/web/rc/installer` after a successful installation!** - -### Enable change password function in Roundcube - -Open `data/web/rc/config/config.inc.php` and enable the password plugin: - -``` -... -$config['plugins'] = array( - 'archive', - 'password', -); -... -``` - -Open `data/web/rc/plugins/password/password.php`, search for `case 'ssha':` and add above: - -``` - case 'ssha256': - $salt = rcube_utils::random_bytes(8); - $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt ); - $prefix = '{SSHA256}'; - break; -``` - -Open `data/web/rc/plugins/password/config.inc.php` and change the following parameters (or add them at the bottom of that file): - -``` -$config['password_driver'] = 'sql'; -$config['password_algorithm'] = 'ssha256'; -$config['password_algorithm_prefix'] = '{SSHA256}'; -$config['password_query'] = "UPDATE mailbox SET password = %P WHERE username = %u"; + +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: +``` +user-to-allow-everything@example.com OK ``` -## MySQL - -### Connect -``` -source mailcow.conf -docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -``` - -### Backup -``` -cd /path/to/mailcow-dockerized -source mailcow.conf -DATE=$(date +"%Y%m%d_%H%M%S") -docker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > backup_${DBNAME}_${DATE}.sql -``` - -### Restore -``` -cd /path/to/mailcow-dockerized -source mailcow.conf -docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} < backup_file.sql +Open `data/conf/postfix/main.cf` and find `smtpd_sender_restrictions`. Prepend `check_sasl_access hash:/opt/postfix/conf/check_sender_access` like this: +``` +smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...] +``` + +Run postmap on check_sasl_access: + +``` +docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access +``` + +Restart the Postfix container. + +## Install Roundcube + +Download Roundcube 1.3.x (beta at the time of Feb 2017) to the web htdocs directory and extract it (here `rc/`): +``` +cd data/web/rc +wget -O - https://github.com/roundcube/roundcubemail/releases/download/1.3-beta/roundcubemail-1.3-beta-complete.tar.gz | tar xfvz - +# Change folder name +mv roundcubemail-1.3* rc +# Change permissions +chown -R root: rc/ +``` + +Create a file `data/web/rc/config/config.inc.php` with the following content. + +**Change the `des_key` parameter to a random value.** It is used to temporarily store your IMAP password. + +``` + array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) +); +$config['enable_installer'] = false; +$config['smtp_conn_options'] = array( +'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true) +); +``` + +Point your browser to `https://myserver/rc/installer` and follow the instructions. +Initialize the database and leave the installer. + +**Delete the directory `data/web/rc/installer` after a successful installation!** + +### Enable change password function in Roundcube + +Open `data/web/rc/config/config.inc.php` and enable the password plugin: + +``` +... +$config['plugins'] = array( + 'archive', + 'password', +); +... +``` + +Open `data/web/rc/plugins/password/password.php`, search for `case 'ssha':` and add above: + +``` + case 'ssha256': + $salt = rcube_utils::random_bytes(8); + $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt ); + $prefix = '{SSHA256}'; + break; +``` + +Open `data/web/rc/plugins/password/config.inc.php` and change the following parameters (or add them at the bottom of that file): + +``` +$config['password_driver'] = 'sql'; +$config['password_algorithm'] = 'ssha256'; +$config['password_algorithm_prefix'] = '{SSHA256}'; +$config['password_query'] = "UPDATE mailbox SET password = %P WHERE username = %u"; +``` + +## MySQL + +### Connect +``` +source mailcow.conf +docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} +``` + +### Backup +``` +cd /path/to/mailcow-dockerized +source mailcow.conf +DATE=$(date +"%Y%m%d_%H%M%S") +docker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > backup_${DBNAME}_${DATE}.sql +``` + +### Restore +``` +cd /path/to/mailcow-dockerized +source mailcow.conf +docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} < backup_file.sql ``` ## Debugging - -You can use `docker-compose logs $service-name` for all containers. - -Run `docker-compose logs` for all logs at once. - -Follow the log output by running docker-compose with `logs -f`. - -## Redirect port 80 to 443 - -Since February the 28th 2017 mailcow does come with port 80 and 443 enabled. - -Open `mailcow.conf` and set `HTTP_BIND=0.0.0.0`. - -Open `data/conf/nginx/site.conf` and add a new "catch-all" site at the top of that file: - -``` -server { - listen 80 default_server; - server_name _; - return 301 https://$host$request_uri; -} -``` - -Restart the stack, changed containers will be updated: - + +You can use `docker-compose logs $service-name` for all containers. + +Run `docker-compose logs` for all logs at once. + +Follow the log output by running docker-compose with `logs -f`. + +## Redirect port 80 to 443 + +Since February the 28th 2017 mailcow does come with port 80 and 443 enabled. + +Open `mailcow.conf` and set `HTTP_BIND=0.0.0.0`. + +Open `data/conf/nginx/site.conf` and add a new "catch-all" site at the top of that file: + +``` +server { + listen 80 default_server; + server_name _; + return 301 https://$host$request_uri; +} +``` + +Restart the stack, changed containers will be updated: + `docker-compose up -d` ## Redis - -### Client - -``` -docker-compose exec redis-mailcow redis-cli + +### Client + +``` +docker-compose exec redis-mailcow redis-cli ``` ## Remove persistent data - -- Remove volume `mysql-vol-1` to remove all MySQL data. -- Remove volume `redis-vol-1` to remove all Redis data. -- Remove volume `vmail-vol-1` to remove all contents of `/var/vmail` mounted to `dovecot-mailcow`. -- Remove volume `dkim-vol-1` to remove all DKIM keys. -- Remove volume `rspamd-vol-1` to remove all Rspamd data. - + +- Remove volume `mysql-vol-1` to remove all MySQL data. +- Remove volume `redis-vol-1` to remove all Redis data. +- Remove volume `vmail-vol-1` to remove all contents of `/var/vmail` mounted to `dovecot-mailcow`. +- Remove volume `dkim-vol-1` to remove all DKIM keys. +- Remove volume `rspamd-vol-1` to remove all Rspamd data. + Running `docker-compose down -v` will **destroy all mailcow: dockerized volumes** and delete any related containers. ## Reset admin password -Reset mailcow admin to `admin:moohoo`: - -1\. Drop admin table -``` -source mailcow.conf -docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TABLE admin;" -``` - -2\. Open mailcow UI to auto-init the db - -## Rspamd +Reset mailcow admin to `admin:moohoo`: + +1\. Drop admin table +``` +source mailcow.conf +docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TABLE admin;" +``` + +2\. Open mailcow UI to auto-init the db + +## Rspamd ### Learn spam and ham @@ -288,126 +287,126 @@ You can also use Rspamd's web ui to learn ham and/or spam. ### CLI tools -``` -docker-compose exec rspamd-mailcow rspamc --help -docker-compose exec rspamd-mailcow rspamadm --help -``` - +``` +docker-compose exec rspamd-mailcow rspamc --help +docker-compose exec rspamd-mailcow rspamadm --help +``` + See [Rspamd documentation](https://rspamd.com/doc/index.html) -## Adjust service configurations +## Adjust service configurations The most important configuration files are mounted from the host into the related containers: - -``` -data/conf -β”œβ”€β”€ bind9 -β”‚Β Β  └── named.conf -β”œβ”€β”€ dovecot -β”‚Β Β  β”œβ”€β”€ dovecot.conf -β”‚Β Β  β”œβ”€β”€ dovecot-master.passwd -β”‚Β Β  β”œβ”€β”€ sieve_after -β”‚Β Β  └── sql -β”‚Β Β  β”œβ”€β”€ dovecot-dict-sql.conf -β”‚Β Β  └── dovecot-mysql.conf -β”œβ”€β”€ mysql -β”‚Β Β  └── my.cnf -β”œβ”€β”€ nginx -β”‚Β Β  β”œβ”€β”€ dynmaps.conf -β”‚Β Β  β”œβ”€β”€ site.conf -β”‚Β Β  └── templates -β”‚Β Β  β”œβ”€β”€ listen_plain.template -β”‚Β Β  β”œβ”€β”€ listen_ssl.template -β”‚Β Β  └── server_name.template -β”œβ”€β”€ pdns -β”‚Β Β  β”œβ”€β”€ pdns_custom.lua -β”‚Β Β  └── recursor.conf -β”œβ”€β”€ postfix -β”‚Β Β  β”œβ”€β”€ main.cf -β”‚Β Β  β”œβ”€β”€ master.cf -β”‚Β Β  β”œβ”€β”€ postscreen_access.cidr -β”‚Β Β  β”œβ”€β”€ smtp_dsn_filter -β”‚Β Β  └── sql -β”‚Β Β  β”œβ”€β”€ mysql_relay_recipient_maps.cf -β”‚Β Β  β”œβ”€β”€ mysql_tls_enforce_in_policy.cf -β”‚Β Β  β”œβ”€β”€ mysql_tls_enforce_out_policy.cf -β”‚Β Β  β”œβ”€β”€ mysql_virtual_alias_domain_catchall_maps.cf -β”‚Β Β  β”œβ”€β”€ mysql_virtual_alias_domain_maps.cf -β”‚Β Β  β”œβ”€β”€ mysql_virtual_alias_maps.cf -β”‚Β Β  β”œβ”€β”€ mysql_virtual_domains_maps.cf -β”‚Β Β  β”œβ”€β”€ mysql_virtual_mailbox_maps.cf -β”‚Β Β  β”œβ”€β”€ mysql_virtual_relay_domain_maps.cf -β”‚Β Β  β”œβ”€β”€ mysql_virtual_sender_acl.cf -β”‚Β Β  └── mysql_virtual_spamalias_maps.cf -β”œβ”€β”€ rmilter -β”‚Β Β  └── rmilter.conf -β”œβ”€β”€ rspamd -β”‚Β Β  β”œβ”€β”€ dynmaps -β”‚Β Β  β”‚Β Β  β”œβ”€β”€ authoritative.php -β”‚Β Β  β”‚Β Β  β”œβ”€β”€ settings.php -β”‚Β Β  β”‚Β Β  β”œβ”€β”€ tags.php -β”‚Β Β  β”‚Β Β  └── vars.inc.php -> ../../../web/inc/vars.inc.php -β”‚Β Β  β”œβ”€β”€ local.d -β”‚Β Β  β”‚Β Β  β”œβ”€β”€ dkim.conf -β”‚Β Β  β”‚Β Β  β”œβ”€β”€ metrics.conf -β”‚Β Β  β”‚Β Β  β”œβ”€β”€ options.inc -β”‚Β Β  β”‚Β Β  β”œβ”€β”€ redis.conf -β”‚Β Β  β”‚Β Β  β”œβ”€β”€ rspamd.conf.local -β”‚Β Β  β”‚Β Β  └── statistic.conf -β”‚Β Β  β”œβ”€β”€ lua -β”‚Β Β  β”‚Β Β  └── rspamd.local.lua -β”‚Β Β  └── override.d -β”‚Β Β  β”œβ”€β”€ logging.inc -β”‚Β Β  β”œβ”€β”€ worker-controller.inc -β”‚Β Β  └── worker-normal.inc -└── sogo - β”œβ”€β”€ sieve.creds - └── sogo.conf - -``` - -Just change the according configuration file on the host and restart the related service: -``` -docker-compose restart service-mailcow -``` - -## Tagging - -Mailbox users can tag their mail address like in `me+facebook@example.org` and choose between to setups to handle this tag: - -1\. Move this message to a subfolder "facebook" (will be created lower case if not existing) - -2\. Prepend the tag to the subject: "[facebook] Subject" - -## Two-factor authentication - -So far two methods for TFA are implemented. Both work with the fantastic [Yubikey](https://www.yubico.com). - -While Yubi OTP needs an active internet connection and an API ID and key, U2F will work with any FIDO U2F USB key out of the box, but can only be used when mailcow is accessed over HTTPS. - -Both methods support multiple YubiKeys. - -As administrator you are able to temporary disable a domain administrators TFA login until they successfully logged in. - -The key used to login will be displayed in green, while other keys remain grey. - -### Yubi OTP - -The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key. -The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret. - -### U2F - -Only Google Chrome (+derivates) and Opera support U2F authentication to this day natively. -For Firefox you will need to install the "U2F Support Add-on" as provided on [mozilla.org](https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/). + +``` +data/conf +β”œβ”€β”€ bind9 +β”‚Β Β  └── named.conf +β”œβ”€β”€ dovecot +β”‚Β Β  β”œβ”€β”€ dovecot.conf +β”‚Β Β  β”œβ”€β”€ dovecot-master.passwd +β”‚Β Β  β”œβ”€β”€ sieve_after +β”‚Β Β  └── sql +β”‚Β Β  β”œβ”€β”€ dovecot-dict-sql.conf +β”‚Β Β  └── dovecot-mysql.conf +β”œβ”€β”€ mysql +β”‚Β Β  └── my.cnf +β”œβ”€β”€ nginx +β”‚Β Β  β”œβ”€β”€ dynmaps.conf +β”‚Β Β  β”œβ”€β”€ site.conf +β”‚Β Β  └── templates +β”‚Β Β  β”œβ”€β”€ listen_plain.template +β”‚Β Β  β”œβ”€β”€ listen_ssl.template +β”‚Β Β  └── server_name.template +β”œβ”€β”€ pdns +β”‚Β Β  β”œβ”€β”€ pdns_custom.lua +β”‚Β Β  └── recursor.conf +β”œβ”€β”€ postfix +β”‚Β Β  β”œβ”€β”€ main.cf +β”‚Β Β  β”œβ”€β”€ master.cf +β”‚Β Β  β”œβ”€β”€ postscreen_access.cidr +β”‚Β Β  β”œβ”€β”€ smtp_dsn_filter +β”‚Β Β  └── sql +β”‚Β Β  β”œβ”€β”€ mysql_relay_recipient_maps.cf +β”‚Β Β  β”œβ”€β”€ mysql_tls_enforce_in_policy.cf +β”‚Β Β  β”œβ”€β”€ mysql_tls_enforce_out_policy.cf +β”‚Β Β  β”œβ”€β”€ mysql_virtual_alias_domain_catchall_maps.cf +β”‚Β Β  β”œβ”€β”€ mysql_virtual_alias_domain_maps.cf +β”‚Β Β  β”œβ”€β”€ mysql_virtual_alias_maps.cf +β”‚Β Β  β”œβ”€β”€ mysql_virtual_domains_maps.cf +β”‚Β Β  β”œβ”€β”€ mysql_virtual_mailbox_maps.cf +β”‚Β Β  β”œβ”€β”€ mysql_virtual_relay_domain_maps.cf +β”‚Β Β  β”œβ”€β”€ mysql_virtual_sender_acl.cf +β”‚Β Β  └── mysql_virtual_spamalias_maps.cf +β”œβ”€β”€ rmilter +β”‚Β Β  └── rmilter.conf +β”œβ”€β”€ rspamd +β”‚Β Β  β”œβ”€β”€ dynmaps +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ authoritative.php +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ settings.php +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ tags.php +β”‚Β Β  β”‚Β Β  └── vars.inc.php -> ../../../web/inc/vars.inc.php +β”‚Β Β  β”œβ”€β”€ local.d +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ dkim.conf +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ metrics.conf +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ options.inc +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ redis.conf +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ rspamd.conf.local +β”‚Β Β  β”‚Β Β  └── statistic.conf +β”‚Β Β  β”œβ”€β”€ lua +β”‚Β Β  β”‚Β Β  └── rspamd.local.lua +β”‚Β Β  └── override.d +β”‚Β Β  β”œβ”€β”€ logging.inc +β”‚Β Β  β”œβ”€β”€ worker-controller.inc +β”‚Β Β  └── worker-normal.inc +└── sogo + β”œβ”€β”€ sieve.creds + └── sogo.conf + +``` + +Just change the according configuration file on the host and restart the related service: +``` +docker-compose restart service-mailcow +``` + +## Tagging + +Mailbox users can tag their mail address like in `me+facebook@example.org` and choose between to setups to handle this tag: + +1\. Move this message to a subfolder "facebook" (will be created lower case if not existing) + +2\. Prepend the tag to the subject: "[facebook] Subject" + +## Two-factor authentication + +So far two methods for TFA are implemented. Both work with the fantastic [Yubikey](https://www.yubico.com). + +While Yubi OTP needs an active internet connection and an API ID and key, U2F will work with any FIDO U2F USB key out of the box, but can only be used when mailcow is accessed over HTTPS. + +Both methods support multiple YubiKeys. + +As administrator you are able to temporary disable a domain administrators TFA login until they successfully logged in. + +The key used to login will be displayed in green, while other keys remain grey. + +### Yubi OTP + +The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key. +The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret. + +### U2F + +Only Google Chrome (+derivates) and Opera support U2F authentication to this day natively. +For Firefox you will need to install the "U2F Support Add-on" as provided on [mozilla.org](https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/). U2F works without an internet connection. ## Why Bind? - -For DNS blacklist lookups and DNSSEC. - -Most systems use either a public or a local caching DNS resolver. -That's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics. -Most if not all providers apply a rate limit based on the DNS resolver that is used to query their service. -Using a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon. + +For DNS blacklist lookups and DNSSEC. + +Most systems use either a public or a local caching DNS resolver. +That's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics. +Most if not all providers apply a rate limit based on the DNS resolver that is used to query their service. +Using a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon. From 14b17e85e44349973f901b2b0a96d1c0b9ed3f83 Mon Sep 17 00:00:00 2001 From: andryyy Date: Tue, 21 Mar 2017 14:45:49 +0100 Subject: [PATCH 27/55] Login button is back... --- data/web/js/mailbox.js | 20 ++++++++++++++++++-- data/web/mailbox.php | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index d38cbc63..e77ba29a 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -27,10 +27,17 @@ $(document).ready(function() { item.mailboxes = item.mboxes_in_domain + " / " + item.max_num_mboxes_for_domain; item.quota = humanFileSize(item.quota_used_in_domain) + " / " + humanFileSize(item.max_quota_for_domain); item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox); + if (role == "admin") { item.action = ''; + } + else { + item.action = '
' + + ' ' + lang.edit + '' + + '
'; + } }); $('#domain_table').footable({ "columns": [ @@ -73,10 +80,19 @@ $(document).ready(function() { $.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 = ''; + } + else { item.action = ''; + } item.in_use = '
' + '
' + item.percent_in_use + '%' + '
'; @@ -90,9 +106,9 @@ $(document).ready(function() { {"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":{"maxWidth":"120px","width":"120px"},"title":lang.msg_num,"breakpoints":"xs sm"}, + {"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","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + {"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, diff --git a/data/web/mailbox.php b/data/web/mailbox.php index c3234e62..570d575e 100644 --- a/data/web/mailbox.php +++ b/data/web/mailbox.php @@ -108,6 +108,8 @@ table.footable>tbody>tr.footable-empty>td { From 95f18f634b63a6404e6dfb92a5fe62a0ea9faa93 Mon Sep 17 00:00:00 2001 From: andryyy Date: Tue, 21 Mar 2017 14:50:58 +0100 Subject: [PATCH 28/55] Add encodeURI --- data/web/js/mailbox.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index e77ba29a..939cc2b0 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -29,13 +29,13 @@ $(document).ready(function() { item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox); if (role == "admin") { item.action = ''; } else { item.action = ''; } }); @@ -82,15 +82,15 @@ $(document).ready(function() { item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox); if (role == "admin") { item.action = ''; } else { item.action = ''; } item.in_use = '
' + @@ -139,8 +139,8 @@ $(document).ready(function() { success: function (data) { $.each(data, function (i, item) { item.action = ''; }); $('#resources_table').footable({ @@ -181,8 +181,8 @@ $(document).ready(function() { success: function (data) { $.each(data, function (i, item) { item.action = ''; }); $('#aliasdomain_table').footable({ @@ -224,8 +224,8 @@ $(document).ready(function() { item.address = '
Catch-All
' + item.address; } item.action = ''; }); $('#alias_table').footable({ From e72a28ebf6284b24e4ef5dfc948571b6a5430ea4 Mon Sep 17 00:00:00 2001 From: andryyy Date: Tue, 21 Mar 2017 15:43:20 +0100 Subject: [PATCH 29/55] Fix ajax errors on empty tables --- data/web/json_api.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/data/web/json_api.php b/data/web/json_api.php index 89ed85a0..f0715466 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -18,6 +18,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } } + else { + echo '{}'; + } break; case "mailbox_table_data": $domains = mailbox_get_domains(); @@ -37,6 +40,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } } + else { + echo '{}'; + } break; case "resource_table_data": $domains = mailbox_get_domains(); @@ -56,6 +62,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } } + else { + echo '{}'; + } break; case "domain_alias_table_data": $domains = mailbox_get_domains(); @@ -75,6 +84,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u 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()); @@ -94,6 +106,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } } + else { + echo '{}'; + } break; case "get_mailbox_details": if (!isset($_GET['object'])) { return false; } From 16d90b86d593795a9d70b7effe62764d3c5b292e Mon Sep 17 00:00:00 2001 From: andryyy Date: Tue, 21 Mar 2017 23:50:00 +0100 Subject: [PATCH 30/55] Use Redis history in Rspamd --- data/conf/rspamd/local.d/rspamd.conf.local | 1 + 1 file changed, 1 insertion(+) diff --git a/data/conf/rspamd/local.d/rspamd.conf.local b/data/conf/rspamd/local.d/rspamd.conf.local index 9f2f8f1d..c51be7bb 100644 --- a/data/conf/rspamd/local.d/rspamd.conf.local +++ b/data/conf/rspamd/local.d/rspamd.conf.local @@ -1 +1,2 @@ # rspamd.conf.local +history_redis {} From aff7fa76711680b3ca7ce0b26138d501db6b26a1 Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 22 Mar 2017 10:44:34 +0100 Subject: [PATCH 31/55] Doc update --- docs/u_and_e.md | 45 +++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/docs/u_and_e.md b/docs/u_and_e.md index 312dd34f..fcafb0d2 100644 --- a/docs/u_and_e.md +++ b/docs/u_and_e.md @@ -1,20 +1,19 @@ -## Add mailcow app buttons +## mailcow UI configuration -Open `data/web/inc/vars.local.inc.php` and add your apps to an array. Default 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_APPS = array( - array( - 'name' => 'SOGo', - 'link' => '/SOGo/' - ), - // array( - // 'name' => 'Roundcube', - // 'link' => '/rc/' - // ), -); -``` +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. ## Backup and restore maildir (simple tar file) @@ -52,24 +51,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. -## 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": -``` - Date: Wed, 22 Mar 2017 10:46:24 +0100 Subject: [PATCH 32/55] Added ru language file (thanks), changes to tables and config option for pagination --- data/web/inc/header.inc.php | 1 + data/web/inc/prerequisites.inc.php | 8 + data/web/inc/vars.inc.php | 5 +- data/web/index.php | 1 + data/web/js/mailbox.js | 9 +- data/web/lang/lang.ru.php | 449 +++++++++++++++++++++++++++++ data/web/mailbox.php | 21 +- 7 files changed, 482 insertions(+), 12 deletions(-) create mode 100644 data/web/lang/lang.ru.php diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php index ceacaf8e..a206a35d 100644 --- a/data/web/inc/header.inc.php +++ b/data/web/inc/header.inc.php @@ -49,6 +49,7 @@
  • > "es"))) ?>">
  • > "nl"))) ?>">
  • > "pt"))) ?>">
  • +
  • > "ru"))) ?>">
  • diff --git a/data/web/index.php b/data/web/index.php index 89b9917c..2e50e4b4 100644 --- a/data/web/index.php +++ b/data/web/index.php @@ -51,6 +51,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
  • > "es"))) ?>">
  • > "nl"))) ?>">
  • > "pt"))) ?>">
  • +
  • > "ru"))) ?>">
  • diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index 939cc2b0..1dc21abe 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -1,5 +1,4 @@ $(document).ready(function() { - // Show element counter for tables $('[data-toggle="tooltip"]').tooltip(); function humanFileSize(bytes) { if(Math.abs(bytes) < 1024) { @@ -55,7 +54,7 @@ $(document).ready(function() { "paging": { "enabled": true, "limit": 5, - "size": 25 + "size": pagination_size }, "filtering": { "enabled": true, @@ -115,7 +114,7 @@ $(document).ready(function() { "paging": { "enabled": true, "limit": 5, - "size": 25 + "size": pagination_size }, "filtering": { "enabled": true, @@ -157,7 +156,7 @@ $(document).ready(function() { "paging": { "enabled": true, "limit": 5, - "size": 25 + "size": pagination_size }, "filtering": { "enabled": true, @@ -197,7 +196,7 @@ $(document).ready(function() { "paging": { "enabled": true, "limit": 5, - "size": 25 + "size": pagination_size }, "filtering": { "enabled": true, diff --git a/data/web/lang/lang.ru.php b/data/web/lang/lang.ru.php new file mode 100644 index 00000000..89deeffa --- /dev/null +++ b/data/web/lang/lang.ru.php @@ -0,0 +1,449 @@ +
    Π’Π°ΠΆΠ½ΠΎ: ΠŸΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° ΠΌΠΎΠΆΠ΅Ρ‚ Π·Π°Π½ΡΡ‚ΡŒ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ врСмя, Π΄ΠΎΠΆΠ΄ΠΈΡ‚Π΅ΡΡŒ Π΅Π΅ окончания."; +$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'] = "НСдопустимый DKIM Π΄ΠΎΠΌΠ΅Π½ ΠΈΠ»ΠΈ сСлСктор"; +$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'] = "Π Π°Π·ΠΌΠ΅Ρ€ ΠΊΠ²ΠΎΡ‚Ρ‹ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ большС 0"; +$lang['danger']['domain_not_found'] = "Π”ΠΎΠΌΠ΅Π½ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½"; +$lang['danger']['max_mailbox_exceeded'] = "ΠŸΡ€Π΅Π²Ρ‹ΡˆΠ΅Π½ΠΎ максимальноС количСство ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹Ρ… ящиков (%d ΠΈΠ· %d)"; +$lang['danger']['mailbox_quota_exceeded'] = "ΠšΠ²ΠΎΡ‚Π° ΠΏΡ€Π΅Π²Ρ‹ΡˆΠ°Π΅Ρ‚ Π»ΠΈΠΌΠΈΡ‚ Π΄ΠΎΠΌΠ΅Π½Π° (максимум %d MB)"; +$lang['danger']['mailbox_quota_left_exceeded'] = "НСдостаточно свободного мСста (мСста ΠΎΡΡ‚Π°Π»ΠΎΡΡŒ: %d MB)"; +$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 MB"; +$lang['danger']['domain_quota_m_in_use'] = "ΠšΠ²ΠΎΡ‚Π° Π΄ΠΎΠΌΠ΅Π½Π° Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ большС ΠΈΠ»ΠΈ Ρ€Π°Π²Π½Π° %s MB"; +$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'] = "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'] = 'Π’Ρ‹ Π·Π½Π°Π»ΠΈ? You can use tags in your email address ("me+privat@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 never classify as spam. Wildcards maybe used.'; +$lang['user']['spamfilter_bl'] = "Π§Π΅Ρ€Π½Ρ‹ΠΉ список"; +$lang['user']['spamfilter_bl_desc'] = 'Blacklisted email addresses to always 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'] = 'Spam 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'] = 'Default values:'; +$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'] = 'Warning: If you decide to enforce encrypted mail transfer, you may lose emails.
    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").
    +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+Facebook@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.
    Attention: 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'] = 'Use a mailcow app to access your mails, calendar, contacts and more.'; +$lang['start']['mailcow_panel'] = 'Start mailcow UI'; +$lang['start']['mailcow_panel_description'] = "ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ интСрфСйс mailcow доступСн для администраторов ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹Ρ… ящиков."; +$lang['start']['mailcow_panel_detail'] = 'Domain administrators create, modify or delete mailboxes and aliases, change domains and read further information about their assigned domains.
    + Mailbox users are able to create time-limited aliases (spam aliases), change their password and spam filter settings.'; +$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 Mozilla Thunderbird.'; +$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.
    +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 Mozilla Thunderbird with the nightly sieve extension.
    Start Thunderbird, open the add-on settings and drop the newly downloaded xpi file into the opened window.
    The server name is %s, use port 4190 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'] = "Π’Ρ‹ вошли ΠΊΠ°ΠΊ %s (Π²Ρ‹ΠΉΡ‚ΠΈ)"; +$lang['header']['logged_in_as_logout_dual'] = 'Π’Ρ‹ вошли ΠΊΠ°ΠΊ %s [%s]'; +$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'] = 'Filter table'; +$lang['mailbox']['yes'] = '✔'; +$lang['mailbox']['no'] = '✘'; +$lang['mailbox']['quota'] = "ΠšΠ²ΠΎΡ‚Π°"; +$lang['mailbox']['in_use'] = "Использовано (%)"; +$lang['mailbox']['msg_num'] = 'Message #'; +$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'] = "Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅: Π’Ρ‹ ΡΠΎΠ±ΠΈΡ€Π°Π΅Ρ‚Π΅ΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ Π΄ΠΎΠΌΠ΅Π½ %s!"; +$lang['delete']['remove_syncjob_warning'] = "Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅: Π’Ρ‹ ΡΠΎΠ±ΠΈΡ€Π°Π΅Ρ‚Π΅ΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ Π·Π°Π΄Π°Π½ΠΈΠ΅ синхронизации для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ %s!"; +$lang['delete']['remove_domainalias_warning'] = "Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅: Π’Ρ‹ ΡΠΎΠ±ΠΈΡ€Π°Π΅Ρ‚Π΅ΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ псСвдоним Π΄ΠΎΠΌΠ΅Π½Π° %s!"; +$lang['delete']['remove_domainadmin_warning'] = "Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅: Π’Ρ‹ ΡΠΎΠ±ΠΈΡ€Π°Π΅Ρ‚Π΅ΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ администратора Π΄ΠΎΠΌΠ΅Π½Π° %s!"; +$lang['delete']['remove_alias_warning'] = "Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅: Π’Ρ‹ ΡΠΎΠ±ΠΈΡ€Π°Π΅Ρ‚Π΅ΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ псСвдоним адрСс %s!"; +$lang['delete']['remove_mailbox_warning'] = "Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅: Π’Ρ‹ ΡΠΎΠ±ΠΈΡ€Π°Π΅Ρ‚Π΅ΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик %s!"; +$lang['delete']['remove_mailbox_details'] = "ΠŸΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡ‡ΠΈΡ‰Π΅Π½ навсСгда!"; +$lang['delete']['remove_resource_warning'] = "Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅: Π’Ρ‹ ΡΠΎΠ±ΠΈΡ€Π°Π΅Ρ‚Π΅ΡΡŒ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ рСсурс %s!"; +$lang['delete']['remove_resource_details'] = "РСсурс Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡ‡ΠΈΡ‰Π΅Π½ навсСгда!"; +$lang['delete']['remove_domain_details'] = "Π­Ρ‚ΠΎ Ρ‚Π°ΠΊΠΆΠ΅ удаляСт псСвдонимы Π΄ΠΎΠΌΠ΅Π½ΠΎΠ².

    Π”ΠΎΠΌΠ΅Π½ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ пустым для удалСния."; +$lang['delete']['remove_syncjob_details'] = "ΠžΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ ΠΈΠ· этого задания синхронизации большС Π½Π΅ Π±ΡƒΠ΄ΡƒΡ‚ ΠΈΠ·Π²Π»Π΅ΠΊΠ°Ρ‚ΡŒΡΡ с ΡƒΠ΄Π°Π»Π΅Π½Π½ΠΎΠ³ΠΎ сСрвСра."; +$lang['delete']['remove_alias_details'] = "ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ большС Π½Π΅ смогут ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ ΠΏΠΎΡ‡Ρ‚Ρƒ ΠΈΠ»ΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ ΠΏΠΎΡ‡Ρ‚Ρƒ с этого адрСса."; +$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
    (0 = ignore age)'; +$lang['edit']['subfolder2'] = 'Sync into subfolder on destination
    (empty = do not use subfolder)'; +$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 (comma-separated)'; +$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'] = "Максимальная ΠΊΠ²ΠΎΡ‚Π° Π½Π° ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик (MB)"; +$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'] = 'Please add a TXT record with the given value to your DNS settings.'; +$lang['edit']['relay_all_info'] = 'If you choose not to relay all recipients, you will need to add a ("blind") mailbox for every single recipient that should be relayed.'; +$lang['edit']['full_name'] = "ПолноС имя"; +$lang['edit']['quota_mb'] = "ΠšΠ²ΠΎΡ‚Π° (MB)"; +$lang['edit']['sender_acl'] = "ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ письма ΠΎΡ‚ (Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ ΠΊΠ°ΠΊΠΎΠΉ адрСс(Π°) ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² качСствС отправитСля)"; +$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']['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'] = "Максимальная ΠΊΠ²ΠΎΡ‚Π° Π½Π° ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик (MB):"; +$lang['add']['domain_quota_m'] = "ΠžΠ±Ρ‰Π°Ρ ΠΊΠ²ΠΎΡ‚Π° Π΄ΠΎΠΌΠ΅Π½Π° (MB):"; +$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'] = 'If you choose not to relay all recipients, you will need to add a ("blind") mailbox for every single recipient that should be relayed.'; +$lang['add']['alias'] = "ПсСвдоним(Ρ‹)"; +$lang['add']['alias_spf_fail'] = 'Π—Π°ΠΌΠ΅Ρ‚ΠΊΠ°: If your chosen destination address is an external mailbox, the receiving mailserver may reject your message due to an SPF failure.'; +$lang['add']['alias_address'] = "ПсСвдоним адрСс(Π°):"; +$lang['add']['alias_address_info'] = 'Full email address/es or @example.com, to catch all messages for a domain (comma-separated). mailcow domains only.'; +$lang['add']['alias_domain_info'] = 'Волько допустимыС Π΄ΠΎΠΌΠ΅Π½Π½Ρ‹Π΅ ΠΈΠΌΠ΅Π½Π° (Ρ‡Π΅Ρ€Π΅Π· Π·Π°ΠΏΡΡ‚ΡƒΡŽ).'; +$lang['add']['target_address'] = "Основной адрСс:"; +$lang['add']['target_address_info'] = "АдрСс(Π°) элСктронной ΠΏΠΎΡ‡Ρ‚Ρ‹ (Ρ‡Π΅Ρ€Π΅Π· Π·Π°ΠΏΡΡ‚ΡƒΡŽ)."; +$lang['add']['alias_domain'] = "ПсСвдоним Π΄ΠΎΠΌΠ΅Π½Π°"; +$lang['add']['select'] = "ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, Π²Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅..."; +$lang['add']['target_domain'] = "Основной Π΄ΠΎΠΌΠ΅Π½:"; +$lang['add']['mailbox'] = "ΠŸΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик"; +$lang['add']['resource'] = "РСсурс"; +$lang['add']['kind'] = "Π’ΠΈΠ΄"; +$lang['add']['mailbox_username'] = "АдрСс Π΄ΠΎ @"; +$lang['add']['full_name'] = "ПолноС имя:"; +$lang['add']['quota_mb'] = "ΠšΠ²ΠΎΡ‚Π° (MB):"; +$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 для вашСго ΠΊΠ»ΡŽΡ‡Π° Ρ‚ΡƒΡ‚.'; +$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'] = "Π–Π΄Π΅ΠΌ USB-устройства...

    ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, Π½Π°ΠΆΠΌΠΈΡ‚Π΅ ΠΊΠ½ΠΎΠΏΠΊΡƒ Π½Π° USB-устройствС U2F."; +$lang['tfa']['waiting_usb_register'] = "Π–Π΄Π΅ΠΌ USB-устройства...

    ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, Π²Π²Π΅Π΄ΠΈΡ‚Π΅ свой ΠΏΠ°Ρ€ΠΎΠ»ΡŒ Π²Ρ‹ΡˆΠ΅ ΠΈ ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€Π΄ΠΈΡ‚Π΅ Ρ€Π΅Π³ΠΈΡΡ‚Ρ€Π°Ρ†ΠΈΡŽ 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.
    Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π½ΠΎΠ²Ρ‹Π΅ элСмСнты Π² inc/vars.local.inc.php, Ρ‡Ρ‚ΠΎ Π±Ρ‹ ΠΈΠΌΠ΅Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒ ΠΈΡ…."; +$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 ΠΊΠ»ΡŽΡ‡Π΅ΠΉ is always dkim."; +$lang['admin']['previous'] = "ΠŸΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π°Ρ страница"; +$lang['admin']['quota_mb'] = "ΠšΠ²ΠΎΡ‚Π° (MB)"; +$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'] = '✔'; +$lang['admin']['no'] = '✘'; +$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'] = "НСт записСй"; +?> \ No newline at end of file diff --git a/data/web/mailbox.php b/data/web/mailbox.php index 570d575e..bc8ca6d2 100644 --- a/data/web/mailbox.php +++ b/data/web/mailbox.php @@ -19,6 +19,12 @@ table.footable>tbody>tr.footable-empty>td { .table-responsive { overflow: visible !important; } +.footer-add-item { + text-align:center; + font-style: italic; + display:block; + padding: 10px; +}
    @@ -36,9 +42,10 @@ table.footable>tbody>tr.footable-empty>td { ?>
    -
    -
    -
    +
    +
    +
    + @@ -54,6 +61,7 @@ table.footable>tbody>tr.footable-empty>td {
    + @@ -69,7 +77,7 @@ table.footable>tbody>tr.footable-empty>td {
    - +
    @@ -84,7 +92,7 @@ table.footable>tbody>tr.footable-empty>td {
    -
    + @@ -100,7 +108,7 @@ table.footable>tbody>tr.footable-empty>td {
    - + @@ -110,6 +118,7 @@ $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"; ?> From 91ba6b2954bebbf80447fbe1267bdc366a48f5af Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 22 Mar 2017 13:35:49 +0100 Subject: [PATCH 33/55] Fix autoconfig --- data/web/autoconfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/web/autoconfig.php b/data/web/autoconfig.php index 563df5d5..ad4857b8 100644 --- a/data/web/autoconfig.php +++ b/data/web/autoconfig.php @@ -6,7 +6,7 @@ header("Content-Type: application/xml"); ';?> - + %EMAILDOMAIN% A mailcow mail server mail server From 271e9a4b56c9c212865df66aeea038874726149a Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 22 Mar 2017 15:07:50 +0100 Subject: [PATCH 34/55] Add hint for password vs enable_password in Rspamd --- docs/first_steps.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/first_steps.md b/docs/first_steps.md index 2202794c..7f835bc4 100644 --- a/docs/first_steps.md +++ b/docs/first_steps.md @@ -64,6 +64,8 @@ docker-compose exec rspamd-mailcow rspamadm pw enable_password = "myhash"; ``` +You can use `password = "myhash";` instead of `enable_password` to disable write-access in the web UI. + 3\. Restart rspamd: ``` docker-compose restart rspamd-mailcow From 78f75deccb92ada72a26e4d9cad6b97069d66ff8 Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 22 Mar 2017 15:08:21 +0100 Subject: [PATCH 35/55] Fix #143 --- docs/u_and_e.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/u_and_e.md b/docs/u_and_e.md index fcafb0d2..0eb9ab03 100644 --- a/docs/u_and_e.md +++ b/docs/u_and_e.md @@ -79,10 +79,10 @@ Open `data/conf/postfix/main.cf` and find `smtpd_sender_restrictions`. Prepend ` smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...] ``` -Run postmap on check_sasl_access: +Run postmap on check_sender_access: ``` -docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access +docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sender_access ``` Restart the Postfix container. @@ -266,6 +266,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. +### 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 ``` From 23cb056ec574c20e1841ebc95e4974053d617e47 Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 22 Mar 2017 15:48:31 +0100 Subject: [PATCH 36/55] Fix server_name for 80 > 433 rediret --- docs/u_and_e.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/u_and_e.md b/docs/u_and_e.md index 0eb9ab03..73714627 100644 --- a/docs/u_and_e.md +++ b/docs/u_and_e.md @@ -215,7 +215,7 @@ Open `data/conf/nginx/site.conf` and add a new "catch-all" site at the top of th ``` server { listen 80 default_server; - server_name _; + include /etc/nginx/conf.d/server_name.active; return 301 https://$host$request_uri; } ``` From 748d69e52e329e124b240cfddaa3725d8a7ee398 Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 22 Mar 2017 18:44:06 +0100 Subject: [PATCH 37/55] Fix check_sender_access --- docs/u_and_e.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/u_and_e.md b/docs/u_and_e.md index 73714627..aa9c2982 100644 --- a/docs/u_and_e.md +++ b/docs/u_and_e.md @@ -74,9 +74,9 @@ Simply create a file `data/conf/postfix/check_sender_access` and enter the follo 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_sender_access hash:/opt/postfix/conf/check_sender_access` like this: ``` -smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...] +smtpd_sender_restrictions = check_sender_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...] ``` Run postmap on check_sender_access: From 8d1e6a5b2d00e58f114ac8258e2a0872aa6ac24a Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 22 Mar 2017 18:52:22 +0100 Subject: [PATCH 38/55] Fix sasl_access info --- docs/u_and_e.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/u_and_e.md b/docs/u_and_e.md index aa9c2982..f348efee 100644 --- a/docs/u_and_e.md +++ b/docs/u_and_e.md @@ -69,20 +69,20 @@ 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. -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 ``` -Open `data/conf/postfix/main.cf` and find `smtpd_sender_restrictions`. Prepend `check_sender_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_sender_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_sender_access: +Run postmap on check_sasl_access: ``` -docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sender_access +docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access ``` Restart the Postfix container. From f5c549f6eacb946ad137b255c7380f950e400120 Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 23 Mar 2017 19:43:52 +0100 Subject: [PATCH 39/55] Fix pagination for aliases --- data/web/js/mailbox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index 1dc21abe..1ddf0c35 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -240,7 +240,7 @@ $(document).ready(function() { "paging": { "enabled": true, "limit": 5, - "size": 5 + "size": pagination_size }, "filtering": { "enabled": true, From e1b44482a7d3774be909581790a2b1e953fc3cea Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 24 Mar 2017 20:38:58 +0100 Subject: [PATCH 40/55] Install methods --- docs/install.md | 50 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/docs/install.md b/docs/install.md index 1274ed84..7c383a63 100644 --- a/docs/install.md +++ b/docs/install.md @@ -49,8 +49,10 @@ The database will be initialized right after a connection to MySQL can be establ ## 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 -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: +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. + +### 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 @@ -61,6 +63,49 @@ git pull 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: ``` @@ -68,6 +113,7 @@ docker-compose pull docker-compose up -d --remove-orphans ``` +### Step 3 Clean-up dangling (unused) images and volumes: ``` From b12e0addc0341b5e33acd08610205c4752bf097e Mon Sep 17 00:00:00 2001 From: andryyy Date: Sat, 25 Mar 2017 18:18:14 +0100 Subject: [PATCH 41/55] Re-add anonymize headers section --- docs/u_and_e.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/u_and_e.md b/docs/u_and_e.md index f348efee..d0a52cc8 100644 --- a/docs/u_and_e.md +++ b/docs/u_and_e.md @@ -15,6 +15,25 @@ mailcow UI configuration parameters can be to... \* To change SOGos default language, you will need to edit `data/conf/sogo/sogo.conf` and replace "English" by your preferred language. +## Anonymize headers + +Save as `data/conf/postfix/mailcow_anonymize_headers.pcre`: + +``` +/^\s*Received:[^\)]+\)\s+\(Authenticated sender:(.+)/ + REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1 +/^\s*User-Agent/ IGNORE +/^\s*X-Enigmail/ IGNORE +/^\s*X-Mailer/ IGNORE +/^\s*X-Originating-IP/ IGNORE +/^\s*X-Forward/ IGNORE +``` + +Add this to `data/conf/postfix/main.cf`: +``` +smtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre +``` + ## Backup and restore maildir (simple tar file) ### Backup From 1b5fb44ada88201481870ddbe2348f7236f27d6e Mon Sep 17 00:00:00 2001 From: "Phoenix Eve C. Aspacio" Date: Sun, 26 Mar 2017 06:20:24 +0800 Subject: [PATCH 42/55] Missed Translation --- data/web/lang/lang.en.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index 0758e538..6b602faf 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -387,7 +387,7 @@ $lang['tfa']['api_register'] = 'mailcow uses the Yubico Cloud API. Please get an $lang['tfa']['u2f'] = "U2F authentication"; $lang['tfa']['hotp'] = "HOTP authentication"; $lang['tfa']['totp'] = "TOTP authentication"; -$lang['tfa']['none'] = "Deaktiviert"; +$lang['tfa']['none'] = "Deactivate"; $lang['tfa']['delete_tfa'] = "Disable TFA"; $lang['tfa']['disable_tfa'] = "Disable TFA until next successful login"; $lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field"; From c3244879970cf7e7125810c8b36b113a193bd6a2 Mon Sep 17 00:00:00 2001 From: "Phoenix Eve C. Aspacio" Date: Sun, 26 Mar 2017 06:52:18 +0800 Subject: [PATCH 43/55] Update mailcow.css --- data/web/css/mailcow.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/data/web/css/mailcow.css b/data/web/css/mailcow.css index a47252bc..1b3a691c 100644 --- a/data/web/css/mailcow.css +++ b/data/web/css/mailcow.css @@ -43,4 +43,10 @@ transform: rotate(359deg); } } -pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;} \ No newline at end of file +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; +} From d5c8f0160cfe6297a0947a324d8d8a375e18e532 Mon Sep 17 00:00:00 2001 From: andryyy Date: Sun, 26 Mar 2017 11:07:50 +0200 Subject: [PATCH 44/55] Go back to apt-stable repo --- data/Dockerfiles/rspamd/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/Dockerfiles/rspamd/Dockerfile b/data/Dockerfiles/rspamd/Dockerfile index 8d0a7f0a..70a0bbab 100644 --- a/data/Dockerfiles/rspamd/Dockerfile +++ b/data/Dockerfiles/rspamd/Dockerfile @@ -9,8 +9,8 @@ RUN dpkg-divert --local --rename --add /sbin/initctl \ && dpkg-divert --local --rename --add /usr/bin/ischroot \ && ln -sf /bin/true /usr/bin/ischroot -RUN apt-key adv --fetch-keys http://rspamd.com/apt/gpg.key \ - && echo "deb http://rspamd.com/apt/ xenial main" > /etc/apt/sources.list.d/rspamd.list \ +RUN apt-key adv --fetch-keys http://rspamd.com/apt-stable/gpg.key \ + && echo "deb http://rspamd.com/apt-stable/ xenial main" > /etc/apt/sources.list.d/rspamd.list \ && apt-get update \ && apt-get -y install rspamd ca-certificates python-pip From 60fa9ab9ddf9f8163f6254dbbc7874618e22b350 Mon Sep 17 00:00:00 2001 From: andryyy Date: Sun, 26 Mar 2017 11:08:07 +0200 Subject: [PATCH 45/55] Fall back to dkim.conf --- data/conf/rspamd/local.d/dkim.conf | 34 +++++++++++++++++++ data/conf/rspamd/local.d/dkim_signing.conf | 38 ---------------------- 2 files changed, 34 insertions(+), 38 deletions(-) create mode 100644 data/conf/rspamd/local.d/dkim.conf delete mode 100644 data/conf/rspamd/local.d/dkim_signing.conf diff --git a/data/conf/rspamd/local.d/dkim.conf b/data/conf/rspamd/local.d/dkim.conf new file mode 100644 index 00000000..c199c6ae --- /dev/null +++ b/data/conf/rspamd/local.d/dkim.conf @@ -0,0 +1,34 @@ +sign_condition =< Date: Sat, 1 Apr 2017 04:24:40 +0300 Subject: [PATCH 46/55] Update lang.ru.php --- data/web/lang/lang.ru.php | 132 +++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/data/web/lang/lang.ru.php b/data/web/lang/lang.ru.php index 89deeffa..32d866db 100644 --- a/data/web/lang/lang.ru.php +++ b/data/web/lang/lang.ru.php @@ -13,14 +13,14 @@ $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'] = "НСдопустимый 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']['mailbox_quota_exceeds_domain_quota'] = "Максимальная ΠΊΠ²ΠΎΡ‚Π° ΠΏΡ€Π΅Π²Ρ‹ΡˆΠ°Π΅Ρ‚ ΠΊΠ²ΠΎΡ‚Ρƒ Π΄ΠΎΠΌΠ΅Π½Π°"; $lang['danger']['object_is_not_numeric'] = "Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ %s Π½Π΅ являСтся числовым"; $lang['success']['domain_added'] = "Π”ΠΎΠ±Π°Π²Π»Π΅Π½ Π΄ΠΎΠΌΠ΅Π½ %s"; $lang['danger']['alias_empty'] = "ПсСвдоним адрСс Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ пустым"; @@ -54,10 +54,10 @@ $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['success']['admin_modified'] = "ИзмСнСния администратора сохранСны"; $lang['danger']['exit_code_not_null'] = "Ошибка: ΠΊΠΎΠ΄ ошибки %d"; $lang['danger']['mailbox_not_available'] = "ΠŸΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик нСдоступСн"; -$lang['danger']['username_invalid'] = "НСльзя ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ это имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ"; +$lang['danger']['username_invalid'] = "НСльзя ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ этот Π»ΠΎΠ³ΠΈΠ½"; $lang['danger']['password_mismatch'] = "Π’Π²Π΅Π΄Π΅Π½Π½Ρ‹Π΅ ΠΏΠ°Ρ€ΠΎΠ»ΠΈ Π½Π΅ ΡΠΎΠ²ΠΏΠ°Π΄Π°ΡŽΡ‚"; $lang['danger']['password_complexity'] = "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ Π½Π΅ соотвСтствуСт трСбованиям"; $lang['danger']['password_empty'] = "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ пустым"; @@ -69,11 +69,11 @@ $lang['danger']['mailbox_invalid_suggest'] = "Имя ΠΏΠΎΡ‡Ρ‚ΠΎΠ²ΠΎΠ³ΠΎ ящик $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'] = "Π Π°Π·ΠΌΠ΅Ρ€ ΠΊΠ²ΠΎΡ‚Ρ‹ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ большС 0"; +$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 MB)"; -$lang['danger']['mailbox_quota_left_exceeded'] = "НСдостаточно свободного мСста (мСста ΠΎΡΡ‚Π°Π»ΠΎΡΡŒ: %d MB)"; +$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 ΡƒΠ΄Π°Π»Π΅Π½"; @@ -83,8 +83,8 @@ $lang['success']['domain_admin_removed'] = "Администратор Π΄ΠΎΠΌΠ΅ $lang['success']['mailbox_removed'] = "ΠŸΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик %s ΡƒΠ΄Π°Π»Π΅Π½"; $lang['success']['eas_reset'] = "Устройства ActiveSync для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ %s Π±Ρ‹Π»ΠΈ ΡΠ±Ρ€ΠΎΡˆΠ΅Π½Ρ‹"; $lang['success']['resource_removed'] = "РСсурс %s ΡƒΠ΄Π°Π»Π΅Π½"; -$lang['danger']['max_quota_in_use'] = "ΠšΠ²ΠΎΡ‚Π° ΠΏΠΎΡ‡Ρ‚ΠΎΠ²ΠΎΠ³ΠΎ ящика Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ большС ΠΈΠ»ΠΈ Ρ€Π°Π²Π½Π° %d MB"; -$lang['danger']['domain_quota_m_in_use'] = "ΠšΠ²ΠΎΡ‚Π° Π΄ΠΎΠΌΠ΅Π½Π° Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ большС ΠΈΠ»ΠΈ Ρ€Π°Π²Π½Π° %s MB"; +$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 отправитСля"; @@ -103,7 +103,7 @@ $lang['user']['mailbox_details'] = "Π”Π°Π½Π½Ρ‹Π΅ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²ΠΎΠ³ΠΎ ящика"; $lang['user']['change_password'] = "Π‘ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ"; $lang['user']['new_password'] = "Новый ΠΏΠ°Ρ€ΠΎΠ»ΡŒ"; $lang['user']['save_changes'] = "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ измСнСния"; -$lang['user']['password_now'] = "Π’Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ (ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ)"; +$lang['user']['password_now'] = "Π’Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ"; $lang['user']['new_password_repeat'] = "ΠŸΠΎΠ²Ρ‚ΠΎΡ€ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ"; $lang['user']['new_password_description'] = "Π’Ρ€Π΅Π±ΠΎΠ²Π°Π½ΠΈΠ΅: 6 символов, Π±ΡƒΠΊΠ²Ρ‹ ΠΈ Ρ†ΠΈΡ„Ρ€Ρ‹."; $lang['user']['did_you_know'] = 'Π’Ρ‹ Π·Π½Π°Π»ΠΈ? You can use tags in your email address ("me+privat@example.com") to move messages to a folder automatically (example: "privat").'; @@ -139,11 +139,11 @@ $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'] = 'Spam score:'; +$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'] = 'Default values:'; +$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'] = 'Warning: If you decide to enforce encrypted mail transfer, you may lose emails.
    Messages to not satisfy the policy will be bounced with a hard fail by the mail system.'; @@ -163,25 +163,25 @@ $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.
    Attention: All elements will be redownloaded!'; $lang['user']['encryption'] = "Π¨ΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΠ΅"; -$lang['user']['username'] = "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ"; +$lang['user']['username'] = "Π›ΠΎΠ³ΠΈΠ½"; $lang['user']['password'] = "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ"; $lang['user']['last_run'] = "ПослСдний запуск"; $lang['user']['excludes'] = "Π˜ΡΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚"; $lang['user']['interval'] = "Π˜Π½Ρ‚Π΅Ρ€Π²Π°Π»"; -$lang['user']['active'] = "ΠΠΊΡ‚ΠΈΠ²Π½ΠΎΡΡ‚ΡŒ"; +$lang['user']['active'] = "Бтатус"; $lang['user']['action'] = "ДСйствия"; -$lang['user']['edit'] = "Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ"; +$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'] = 'Use a mailcow app to access your mails, calendar, contacts and more.'; +$lang['start']['mailcow_apps_detail'] = "ΠŸΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ для доступа ΠΊ своСй элСктронной ΠΏΠΎΡ‡Ρ‚Π΅, ΠΊΠ°Π»Π΅Π½Π΄Π°Ρ€ΡŽ, ΠΊΠΎΠ½Ρ‚Π°ΠΊΡ‚Π°ΠΌ ΠΈ Ρ‚.Π΄."; $lang['start']['mailcow_panel'] = 'Start mailcow UI'; $lang['start']['mailcow_panel_description'] = "ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ интСрфСйс mailcow доступСн для администраторов ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹Ρ… ящиков."; -$lang['start']['mailcow_panel_detail'] = 'Domain administrators create, modify or delete mailboxes and aliases, change domains and read further information about their assigned domains.
    - Mailbox users are able to create time-limited aliases (spam aliases), change their password and spam filter settings.'; +$lang['start']['mailcow_panel_detail'] = "Администраторы ΠΌΠΎΠ³ΡƒΡ‚ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ, ΠΈΠ·ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΈΠ»ΠΈ ΡƒΠ΄Π°Π»ΡΡ‚ΡŒ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹Π΅ ящики, псСвдонимы ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ административныС настройки.
    + ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ ΠΌΠΎΠ³ΡƒΡ‚ ΠΈΠ·ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ, ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ псСвдонимы (спам псСвдонимы), Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹ спама ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ доступныС настройки своСго профиля."; $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 Mozilla Thunderbird.'; @@ -193,13 +193,13 @@ $lang['start']['managesieve_badge'] = "Π€ΠΈΠ»ΡŒΡ‚Ρ€ ΠΏΠΎΡ‡Ρ‚Ρ‹"; $lang['start']['managesieve_description'] = 'Please use Mozilla Thunderbird with the nightly sieve extension.
    Start Thunderbird, open the add-on settings and drop the newly downloaded xpi file into the opened window.
    The server name is %s, use port 4190 if you are asked for. The login data match your email login.'; $lang['start']['service'] = "БСрвисы"; $lang['start']['encryption'] = "ΠœΠ΅Ρ‚ΠΎΠ΄ ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΡ"; -$lang['start']['help'] = "ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ/Π‘ΠΊΡ€Ρ‹Ρ‚ΡŒ панСль ΠΏΠΎΠΌΠΎΡ‰ΠΈ"; +$lang['start']['help'] = "Π‘ΠΏΡ€Π°Π²ΠΊΠ°"; $lang['start']['hostname'] = "Имя хоста"; $lang['start']['port'] = "ΠŸΠΎΡ€Ρ‚"; $lang['start']['footer'] = ''; -$lang['header']['mailcow_settings'] = "ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ"; -$lang['header']['administration'] = "АдминистрированиС"; -$lang['header']['mailboxes'] = "ΠŸΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹Π΅ ящики"; +$lang['header']['mailcow_settings'] = "МСню"; +$lang['header']['administration'] = "АдминистративныС настройки"; +$lang['header']['mailboxes'] = "Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΌ сСрвСром"; $lang['header']['user_settings'] = "Настройки ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ"; $lang['header']['login'] = "Π›ΠΎΠ³ΠΈΠ½"; $lang['header']['logged_in_as_logout'] = "Π’Ρ‹ вошли ΠΊΠ°ΠΊ %s (Π²Ρ‹ΠΉΡ‚ΠΈ)"; @@ -218,23 +218,23 @@ $lang['mailbox']['mailboxes'] = "ΠŸΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹Π΅ ящики"; $lang['mailbox']['resources'] = "РСсурсы"; $lang['mailbox']['mailbox_quota'] = "Макс. ΠΊΠ²ΠΎΡ‚Π° ΠΏΠΎΡ‡Ρ‚ΠΎΠ²ΠΎΠ³ΠΎ ящика"; $lang['mailbox']['domain_quota'] = "ΠšΠ²ΠΎΡ‚Π°"; -$lang['mailbox']['active'] = "ΠΠΊΡ‚ΠΈΠ²Π½ΠΎΡΡ‚ΡŒ"; +$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'] = 'Filter table'; +$lang['mailbox']['username'] = "ΠŸΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик"; +$lang['mailbox']['fname'] = "Имя"; +$lang['mailbox']['filter_table'] = "Поиск"; $lang['mailbox']['yes'] = '✔'; $lang['mailbox']['no'] = '✘'; $lang['mailbox']['quota'] = "ΠšΠ²ΠΎΡ‚Π°"; -$lang['mailbox']['in_use'] = "Использовано (%)"; -$lang['mailbox']['msg_num'] = 'Message #'; +$lang['mailbox']['in_use'] = "Занято"; +$lang['mailbox']['msg_num'] = "Письма"; $lang['mailbox']['remove'] = "Π£Π΄Π°Π»ΠΈΡ‚ΡŒ"; -$lang['mailbox']['edit'] = "Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ"; +$lang['mailbox']['edit'] = "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ"; $lang['mailbox']['archive'] = "Архив"; $lang['mailbox']['no_record'] = 'No record for object %s'; $lang['mailbox']['no_record_single'] = "НСт записи"; @@ -260,9 +260,9 @@ $lang['delete']['remove_syncjob_details'] = "ΠžΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ ΠΈΠ· этого Π·Π° $lang['delete']['remove_alias_details'] = "ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ большС Π½Π΅ смогут ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ ΠΏΠΎΡ‡Ρ‚Ρƒ ΠΈΠ»ΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ ΠΏΠΎΡ‡Ρ‚Ρƒ с этого адрСса."; $lang['delete']['remove_button'] = "Π£Π΄Π°Π»ΠΈΡ‚ΡŒ"; $lang['delete']['previous'] = "ΠŸΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π°Ρ страница"; -$lang['edit']['syncjob'] = "Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π·Π°Π΄Π°Π½ΠΈΠ΅ синхронизации"; +$lang['edit']['syncjob'] = "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π·Π°Π΄Π°Π½ΠΈΠ΅ синхронизации"; $lang['edit']['save'] = "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ"; -$lang['edit']['username'] = "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ"; +$lang['edit']['username'] = "Π›ΠΎΠ³ΠΈΠ½"; $lang['edit']['hostname'] = "Имя хоста"; $lang['edit']['encryption'] = "Π¨ΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΠ΅"; $lang['edit']['maxage'] = 'Maximum age of messages in days that will be polled from remote
    (0 = ignore age)'; @@ -281,7 +281,7 @@ $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']['domain'] = "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π΄ΠΎΠΌΠ΅Π½"; $lang['edit']['alias_domain'] = "ПсСвдоним Π΄ΠΎΠΌΠ΅Π½Π°"; $lang['edit']['edit_alias_domain'] = "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ псСвдоним Π΄ΠΎΠΌΠ΅Π½Π°"; $lang['edit']['domains'] = "Π”ΠΎΠΌΠ΅Π½Ρ‹"; @@ -290,20 +290,20 @@ $lang['edit']['alias'] = "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ псСвдоним"; $lang['edit']['mailbox'] = "Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²ΠΎΠ³ΠΎ ящика"; $lang['edit']['description'] = "ОписаниС"; $lang['edit']['max_aliases'] = "ΠœΠ°ΠΊΡΠΈΠΌΡƒΠΌ псСвдонимов"; -$lang['edit']['max_quota'] = "Максимальная ΠΊΠ²ΠΎΡ‚Π° Π½Π° ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик (MB)"; +$lang['edit']['max_quota'] = "Максимальная ΠΊΠ²ΠΎΡ‚Π° Π½Π° ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик (MiB)"; $lang['edit']['domain_quota'] = "ΠšΠ²ΠΎΡ‚Π° Π΄ΠΎΠΌΠ΅Π½Π°"; -$lang['edit']['backup_mx_options'] = "Настройки Ρ€Π΅Π·Π΅Ρ€Π²Π½ΠΎΠ³ΠΎ MX:"; +$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'] = 'Please add a TXT record with the given value to your DNS settings.'; $lang['edit']['relay_all_info'] = 'If you choose not to relay all recipients, you will need to add a ("blind") mailbox for every single recipient that should be relayed.'; -$lang['edit']['full_name'] = "ПолноС имя"; -$lang['edit']['quota_mb'] = "ΠšΠ²ΠΎΡ‚Π° (MB)"; +$lang['edit']['full_name'] = "Имя"; +$lang['edit']['quota_mb'] = "ΠšΠ²ΠΎΡ‚Π° (MiB)"; $lang['edit']['sender_acl'] = "ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ письма ΠΎΡ‚ (Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ ΠΊΠ°ΠΊΠΎΠΉ адрСс(Π°) ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² качСствС отправитСля)"; $lang['edit']['sender_acl_info'] = "ΠŸΡΠ΅Π²Π΄ΠΎΠ½ΠΈΠΌΡ‹ Π½Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½Ρ‹"; -$lang['edit']['dkim_txt_name'] = "Имя TXT записи:"; -$lang['edit']['dkim_txt_value'] = "Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ TXT записи:"; +$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"; @@ -313,7 +313,7 @@ $lang['edit']['resource'] = "РСсурс"; $lang['add']['syncjob'] = "Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π·Π°Π΄Π°Π½ΠΈΠ΅ синхронизации"; $lang['add']['syncjob_hint'] = "ΠŸΠΎΠΌΠ½ΠΈΡ‚Π΅, Ρ‡Ρ‚ΠΎ ΠΏΠ°Ρ€ΠΎΠ»ΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ сохранСны ΠΊΠ°ΠΊ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΉ тСкст!"; $lang['add']['hostname'] = "Имя хоста"; -$lang['add']['username'] = "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ"; +$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)'; @@ -325,43 +325,43 @@ $lang['add']['domain'] = "Π”ΠΎΠΌΠ΅Π½"; $lang['add']['active'] = "Активный"; $lang['add']['multiple_bookings'] = 'Multiple bookings'; $lang['add']['save'] = "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ измСнСния"; -$lang['add']['description'] = "ОписаниС:"; -$lang['add']['max_aliases'] = "ΠœΠ°ΠΊΡΠΈΠΌΡƒΠΌ псСвдонимов:"; +$lang['add']['description'] = "ОписаниС"; +$lang['add']['max_aliases'] = "ΠœΠ°ΠΊΡΠΈΠΌΡƒΠΌ псСвдонимов"; $lang['add']['resource_name'] = "Имя рСсурса"; -$lang['add']['max_mailboxes'] = "ΠœΠ°ΠΊΡΠΈΠΌΡƒΠΌ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹Ρ… ящиков:"; -$lang['add']['mailbox_quota_m'] = "Максимальная ΠΊΠ²ΠΎΡ‚Π° Π½Π° ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик (MB):"; -$lang['add']['domain_quota_m'] = "ΠžΠ±Ρ‰Π°Ρ ΠΊΠ²ΠΎΡ‚Π° Π΄ΠΎΠΌΠ΅Π½Π° (MB):"; -$lang['add']['backup_mx_options'] = "Настройки Ρ€Π΅Π·Π΅Ρ€Π²Π½ΠΎΠ³ΠΎ MX:"; +$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'] = 'If you choose not to relay all recipients, you will need to add a ("blind") mailbox for every single recipient that should be relayed.'; $lang['add']['alias'] = "ПсСвдоним(Ρ‹)"; $lang['add']['alias_spf_fail'] = 'Π—Π°ΠΌΠ΅Ρ‚ΠΊΠ°: If your chosen destination address is an external mailbox, the receiving mailserver may reject your message due to an SPF failure.'; -$lang['add']['alias_address'] = "ПсСвдоним адрСс(Π°):"; +$lang['add']['alias_address'] = "ПсСвдоним адрСс(Π°)"; $lang['add']['alias_address_info'] = 'Full email address/es or @example.com, to catch all messages for a domain (comma-separated). mailcow domains only.'; $lang['add']['alias_domain_info'] = 'Волько допустимыС Π΄ΠΎΠΌΠ΅Π½Π½Ρ‹Π΅ ΠΈΠΌΠ΅Π½Π° (Ρ‡Π΅Ρ€Π΅Π· Π·Π°ΠΏΡΡ‚ΡƒΡŽ).'; -$lang['add']['target_address'] = "Основной адрСс:"; +$lang['add']['target_address'] = "Основной адрСс"; $lang['add']['target_address_info'] = "АдрСс(Π°) элСктронной ΠΏΠΎΡ‡Ρ‚Ρ‹ (Ρ‡Π΅Ρ€Π΅Π· Π·Π°ΠΏΡΡ‚ΡƒΡŽ)."; $lang['add']['alias_domain'] = "ПсСвдоним Π΄ΠΎΠΌΠ΅Π½Π°"; $lang['add']['select'] = "ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, Π²Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅..."; -$lang['add']['target_domain'] = "Основной Π΄ΠΎΠΌΠ΅Π½:"; +$lang['add']['target_domain'] = "Основной Π΄ΠΎΠΌΠ΅Π½"; $lang['add']['mailbox'] = "ΠŸΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹ΠΉ ящик"; $lang['add']['resource'] = "РСсурс"; $lang['add']['kind'] = "Π’ΠΈΠ΄"; -$lang['add']['mailbox_username'] = "АдрСс Π΄ΠΎ @"; -$lang['add']['full_name'] = "ПолноС имя:"; -$lang['add']['quota_mb'] = "ΠšΠ²ΠΎΡ‚Π° (MB):"; +$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']['password'] = "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ"; +$lang['add']['password_repeat'] = "ΠŸΠΎΠ²Ρ‚ΠΎΡ€ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ"; $lang['add']['previous'] = "ΠŸΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π°Ρ страница"; -$lang['add']['restart_sogo_hint'] = "ПослС добавлСния Π½ΠΎΠ²ΠΎΠ³ΠΎ Π΄ΠΎΠΌΠ΅Π½Π° потрСбуСтся ΠΏΠ΅Ρ€Π΅Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ SOGo!"; +$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']['username'] = "Π›ΠΎΠ³ΠΈΠ½"; $lang['login']['password'] = "ΠŸΠ°Ρ€ΠΎΠ»ΡŒ"; $lang['login']['reset_password'] = "Π‘Π±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ"; $lang['login']['login'] = "Π’ΠΎΠΉΡ‚ΠΈ"; @@ -404,19 +404,19 @@ $lang['admin']['public_folder_pusf_text'] = 'A "per-user seen flag"-enabled syst $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_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 ΠΊΠ»ΡŽΡ‡Π΅ΠΉ is always dkim."; +$lang['admin']['dkim_key_hint'] = "Π‘Π΅Π»Π΅ΠΊΡ‚ΠΎΡ€ для DKIM ΠΊΠ»ΡŽΡ‡Π΅ΠΉ dkim. Подсказка: ΠŸΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ записи Π½Π° DNS-сСрвСрС, ΡƒΠΊΠ°ΠΆΠΈΡ‚Π΅ Имя TXT записи dkim._domainkey."; $lang['admin']['previous'] = "ΠŸΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π°Ρ страница"; -$lang['admin']['quota_mb'] = "ΠšΠ²ΠΎΡ‚Π° (MB)"; -$lang['admin']['sender_acl'] = "Π Π°Π·Ρ€Π΅ΡˆΠΈΡ‚ΡŒ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ письма ΠΎΡ‚:"; +$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']['msg_size_limit_details'] = "ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ ограничСния ΠΏΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ Postfix ΠΈ Π²Π΅Π±-сСрвСр"; $lang['admin']['save'] = "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ измСнСния"; $lang['admin']['maintenance'] = "ВСхничСскоС обслуТиваниС ΠΈ информация"; $lang['admin']['sys_info'] = "БистСмная информация"; @@ -431,19 +431,19 @@ $lang['admin']['action'] = "ДСйствия"; $lang['admin']['add_domain_admin'] = "Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ администратора Π΄ΠΎΠΌΠ΅Π½Π°"; $lang['admin']['admin_domains'] = "Π”ΠΎΠΌΠ΅Π½"; $lang['admin']['domain_admins'] = "Администраторы Π΄ΠΎΠΌΠ΅Π½Π°"; -$lang['admin']['username'] = "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ"; -$lang['admin']['edit'] = "Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ"; +$lang['admin']['username'] = "Π›ΠΎΠ³ΠΈΠ½"; +$lang['admin']['edit'] = "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ"; $lang['admin']['remove'] = "Π£Π΄Π°Π»ΠΈΡ‚ΡŒ"; $lang['admin']['save'] = "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ измСнСния"; $lang['admin']['admin'] = "Администратор"; -$lang['admin']['admin_details'] = "Π Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ администратора"; +$lang['admin']['admin_details'] = "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ администратора"; $lang['admin']['unchanged_if_empty'] = "Если Π±Π΅Π· ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ, ΠΎΡΡ‚Π°Π²ΡŒΡ‚Π΅ ΠΏΠΎΠ»Π΅ пустым"; $lang['admin']['yes'] = '✔'; $lang['admin']['no'] = '✘'; $lang['admin']['access'] = "ΠŸΡ€Π°Π²Π° доступа"; -$lang['admin']['invalid_max_msg_size'] = "НСвСрный ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ сообщСния"; +$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'] = "НСт записСй"; -?> \ No newline at end of file +?> From 45bb5fbad49174e1559fc2543a41e5017f3d203b Mon Sep 17 00:00:00 2001 From: Aleksandr Kliushenok Date: Sat, 1 Apr 2017 07:46:47 +0300 Subject: [PATCH 47/55] Update lang.ru.php --- data/web/lang/lang.ru.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/web/lang/lang.ru.php b/data/web/lang/lang.ru.php index 32d866db..9fc42b45 100644 --- a/data/web/lang/lang.ru.php +++ b/data/web/lang/lang.ru.php @@ -177,7 +177,7 @@ $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_apps_detail'] = "ΠŸΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ для доступа ΠΊ элСктронной ΠΏΠΎΡ‡Ρ‚Π΅, ΠΊΠ°Π»Π΅Π½Π΄Π°Ρ€ΡŽ, ΠΊΠΎΠ½Ρ‚Π°ΠΊΡ‚Π°ΠΌ ΠΈ Ρ‚.Π΄."; $lang['start']['mailcow_panel'] = 'Start mailcow UI'; $lang['start']['mailcow_panel_description'] = "ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ интСрфСйс mailcow доступСн для администраторов ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹Ρ… ящиков."; $lang['start']['mailcow_panel_detail'] = "Администраторы ΠΌΠΎΠ³ΡƒΡ‚ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ, ΠΈΠ·ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΈΠ»ΠΈ ΡƒΠ΄Π°Π»ΡΡ‚ΡŒ ΠΏΠΎΡ‡Ρ‚ΠΎΠ²Ρ‹Π΅ ящики, псСвдонимы ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ административныС настройки.
    From d12642c47ed807c7490b5115aaee03c8d26f97ad Mon Sep 17 00:00:00 2001 From: Aleksandr Kliushenok Date: Sat, 1 Apr 2017 08:17:34 +0300 Subject: [PATCH 48/55] Update lang.ru.php --- data/web/lang/lang.ru.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/data/web/lang/lang.ru.php b/data/web/lang/lang.ru.php index 9fc42b45..945635c7 100644 --- a/data/web/lang/lang.ru.php +++ b/data/web/lang/lang.ru.php @@ -94,8 +94,8 @@ $lang['danger']['spam_alias_max_exceeded'] = "ΠŸΡ€Π΅Π²Ρ‹ΡˆΠ΅Π½ΠΈΠ΅ максим $lang['danger']['validity_missing'] = "ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, Π½Π°Π·Π½Π°Ρ‡ΡŒΡ‚Π΅ срок дСйствия"; $lang['user']['on'] = "Π’ΠΊΠ»."; $lang['user']['off'] = "Π’Ρ‹ΠΊΠ»."; -$lang['user']['messages'] = "messages"; // "123 messages" -$lang['user']['in_use'] = "Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ"; +$lang['user']['messages'] = "писСм"; // "123 messages" +$lang['user']['in_use'] = "Занято"; $lang['user']['user_change_fn'] = ""; $lang['user']['user_settings'] = "Настройки ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ"; $lang['user']['mailbox_settings'] = "Настройки ΠΏΠΎΡ‡Ρ‚ΠΎΠ²ΠΎΠ³ΠΎ ящика"; @@ -123,11 +123,11 @@ $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']['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 never classify as spam. Wildcards maybe used.'; From 1602aee42406963d04e2119ea7ef38456829234d Mon Sep 17 00:00:00 2001 From: Aleksandr Kliushenok Date: Sat, 1 Apr 2017 08:50:33 +0300 Subject: [PATCH 49/55] Update add.php --- data/web/add.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/web/add.php b/data/web/add.php index 664d96ad..9361d14b 100644 --- a/data/web/add.php +++ b/data/web/add.php @@ -292,7 +292,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] ==
    - +
    From 1cc1bfa3b63a108f70ec67f6b5a7778e03b05274 Mon Sep 17 00:00:00 2001 From: Aleksandr Kliushenok Date: Sat, 1 Apr 2017 08:55:53 +0300 Subject: [PATCH 50/55] Update lang.ru.php --- data/web/lang/lang.ru.php | 1 + 1 file changed, 1 insertion(+) diff --git a/data/web/lang/lang.ru.php b/data/web/lang/lang.ru.php index 945635c7..e2502b83 100644 --- a/data/web/lang/lang.ru.php +++ b/data/web/lang/lang.ru.php @@ -313,6 +313,7 @@ $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'] = "Π˜Π½Ρ‚Π΅Ρ€Π²Π°Π» опроса (Π² ΠΌΠΈΠ½ΡƒΡ‚Π°Ρ…)"; From 563a8bda61d7137d0ea8795d55c0083799ff74e8 Mon Sep 17 00:00:00 2001 From: Aleksandr Kliushenok Date: Sat, 1 Apr 2017 08:58:08 +0300 Subject: [PATCH 51/55] Update lang.de.php --- data/web/lang/lang.de.php | 1 + 1 file changed, 1 insertion(+) diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index 160a306e..eef252e9 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -318,6 +318,7 @@ $lang['edit']['resource'] = 'Ressource'; $lang['add']['syncjob'] = 'Sync-Job erstellen'; $lang['add']['syncjob_hint'] = 'PasswΓΆrter werden unverschlΓΌsselt abgelegt!'; $lang['add']['hostname'] = 'Servername'; +$lang['add']['port'] = 'Port'; $lang['add']['username'] = 'Benutzername'; $lang['add']['enc_method'] = 'VerschlΓΌsselungsmethode'; $lang['add']['maxage'] = 'Maximum age of messages that will be polled from remote (0 = ignore age)'; From 80774224c223f012c5ed402cda5e5700e39aa12a Mon Sep 17 00:00:00 2001 From: Aleksandr Kliushenok Date: Sat, 1 Apr 2017 08:59:05 +0300 Subject: [PATCH 52/55] Update lang.en.php --- data/web/lang/lang.en.php | 1 + 1 file changed, 1 insertion(+) diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index 6b602faf..a9f2f51c 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -322,6 +322,7 @@ $lang['edit']['resource'] = 'Resource'; $lang['add']['syncjob'] = 'Add sync job'; $lang['add']['syncjob_hint'] = 'Be aware that passwords need to be saved plain-text!'; $lang['add']['hostname'] = 'Hostname'; +$lang['add']['port'] = 'Port'; $lang['add']['username'] = 'Username'; $lang['add']['enc_method'] = 'Encryption method'; $lang['add']['mins_interval'] = 'Polling interval (minutes)'; From cb742b5491cb7cabc6e2e90486cf5adf154a3b04 Mon Sep 17 00:00:00 2001 From: Aleksandr Kliushenok Date: Sat, 1 Apr 2017 09:01:35 +0300 Subject: [PATCH 53/55] Update lang.es.php --- data/web/lang/lang.es.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/web/lang/lang.es.php b/data/web/lang/lang.es.php index dbe3f3d0..2985ac1f 100644 --- a/data/web/lang/lang.es.php +++ b/data/web/lang/lang.es.php @@ -299,7 +299,7 @@ $lang['add']['password'] = 'ConstraseΓ±a:'; $lang['add']['password_repeat'] = 'ConfirmaciΓ³n de contraseΓ±a (repetir):'; $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']['port'] = 'Port'; $lang['login']['title'] = 'Inicio de sesiΓ³n'; $lang['login']['administration'] = 'AdministraciΓ³n'; $lang['login']['administration_details'] = 'Por favor utiliza tu inicio de sesiΓ³n de Administrador para realizar tareas administrativas.'; From 2c398c727e5e1c922746072521285d8fe426065d Mon Sep 17 00:00:00 2001 From: Aleksandr Kliushenok Date: Sat, 1 Apr 2017 09:02:38 +0300 Subject: [PATCH 54/55] Update lang.nl.php --- data/web/lang/lang.nl.php | 1 + 1 file changed, 1 insertion(+) diff --git a/data/web/lang/lang.nl.php b/data/web/lang/lang.nl.php index 35192ddb..0118817e 100644 --- a/data/web/lang/lang.nl.php +++ b/data/web/lang/lang.nl.php @@ -259,6 +259,7 @@ $lang['edit']['previous'] = 'Vorige pagina'; $lang['edit']['unchanged_if_empty'] = 'Leeg laten indien niet veranderd.'; $lang['edit']['dont_check_sender_acl'] = 'Geen zenderverificatie uitvoeren voor domein %s.'; +$lang['add']['port'] = 'Port'; $lang['add']['title'] = 'Object toevoegen'; $lang['add']['domain'] = 'Domein'; $lang['add']['active'] = 'Actief'; From e7631e47277b98b18e96a080a1ec3fc8129ca455 Mon Sep 17 00:00:00 2001 From: Aleksandr Kliushenok Date: Sat, 1 Apr 2017 09:03:31 +0300 Subject: [PATCH 55/55] Update lang.pt.php --- data/web/lang/lang.pt.php | 1 + 1 file changed, 1 insertion(+) diff --git a/data/web/lang/lang.pt.php b/data/web/lang/lang.pt.php index f8c0e330..689faaa9 100644 --- a/data/web/lang/lang.pt.php +++ b/data/web/lang/lang.pt.php @@ -279,6 +279,7 @@ $lang['add']['select_domain'] = 'Selecione um domΓ­nio antes'; $lang['add']['password'] = 'Senha:'; $lang['add']['password_repeat'] = 'Confirmar a senha (repetir):'; $lang['add']['previous'] = 'Voltar'; +$lang['add']['port'] = 'Port'; $lang['login']['title'] = 'Entrar'; $lang['login']['administration'] = 'AdministraΓ§Γ£o'; $lang['login']['administration_details'] = 'Utilize o login de Administrador para efetuar tarefas de administraΓ§Γ£o.';