diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..24db8c03 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,18 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security + - enhancement +# Label to use when marking an issue as stale +staleLabel: dunno +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/README.md b/README.md index 42b2a3f1..ec500d9c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ # mailcow: dockerized - 🐮 + 🐋 = 💕 -[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JWBSYHF4SMC68) +## Want to support mailcow? -**mailcow Bitcoin donations:** 1E5rgzgA1sS3QH7r1ToWxRC3GEavfsGMrx +Donate via **PayPal** [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JWBSYHF4SMC68) or via **Liberapay** [![Liberapay.com](https://mailcow.email/img/lp.png)](https://liberapay.com/mailcow) + +Or just spread the word: moo. + +## Info and documentation Please see [the official documentation](https://mailcow.github.io/mailcow-dockerized-docs/) for instructions. diff --git a/data/Dockerfiles/acme/docker-entrypoint.sh b/data/Dockerfiles/acme/docker-entrypoint.sh index 37e150f6..0c209907 100755 --- a/data/Dockerfiles/acme/docker-entrypoint.sh +++ b/data/Dockerfiles/acme/docker-entrypoint.sh @@ -13,8 +13,12 @@ log_f() { elif [[ ${2} != "redis_only" ]]; then echo "$(date) - ${1}" fi - redis-cli -h redis LPUSH ACME_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${1}" | \ - tr '%&;$"_[]{}-\r\n' ' ')\"}" > /dev/null + if [[ ${3} == "b64" ]]; then + redis-cli -h redis LPUSH ACME_LOG "{\"time\":\"$(date +%s)\",\"message\":\"base64,$(printf '%s' "${1}")\"}" > /dev/null + else + redis-cli -h redis LPUSH ACME_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${1}" | \ + tr '%&;$"_[]{}-\r\n' ' ')\"}" > /dev/null + fi } if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then @@ -125,7 +129,7 @@ else fi log_f "Waiting for database... " -while ! mysqladmin ping --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do sleep 2 done log_f "Initializing, please wait... " @@ -324,10 +328,10 @@ while true; do -k ${ACME_BASE}/acme/private/privkey.pem \ -c ${ACME_BASE}/acme \ ${ALL_VALIDATED[*]} 2>&1 | tee /dev/fd/5) - case "$?" in 0) # new certs - log_f "${ACME_RESPONSE}" redis_only + ACME_RESPONSE_B64=$(echo ${ACME_RESPONSE} | openssl enc -e -A -base64) + log_f "${ACME_RESPONSE_B64}" redis_only b64 # cp the new certificates and keys cp ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem cp ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/key.pem @@ -341,7 +345,8 @@ while true; do restart_containers ${CONTAINERS_RESTART[*]} ;; 1) # failure - log_f "${ACME_RESPONSE}" redis_only + ACME_RESPONSE_B64=$(echo ${ACME_RESPONSE} | openssl enc -e -A -base64) + log_f "${ACME_RESPONSE_B64}" redis_only b64 if [[ $ACME_RESPONSE =~ "No registration exists" ]]; then log_f "Registration keys are invalid, deleting old keys and restarting..." rm ${ACME_BASE}/acme/private/account.key @@ -370,7 +375,8 @@ while true; do exec $(readlink -f "$0") ;; 2) # no change - log_f "${ACME_RESPONSE}" redis_only + ACME_RESPONSE_B64=$(echo ${ACME_RESPONSE} | openssl enc -e -A -base64) + log_f "${ACME_RESPONSE_B64}" redis_only b64 if ! diff ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem; then log_f "Certificate was not changed, but active certificate does not match the verified certificate, fixing and restarting containers..." cp ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem @@ -387,7 +393,8 @@ while true; do [[ ${TRIGGER_RESTART} == 1 ]] && restart_containers ${CONTAINERS_RESTART[*]} ;; *) # unspecified - log_f "${ACME_RESPONSE}" redis_only + ACME_RESPONSE_B64=$(echo ${ACME_RESPONSE} | openssl enc -e -A -base64) + log_f "${ACME_RESPONSE_B64}" redis_only b64 if [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ]] && [[ -f ${ACME_BASE}/acme/private/${DATE}.bak/privkey.pem ]]; then log_f "Error requesting certificate, restoring previous certificate from backup and restarting containers...." cp ${ACME_BASE}/acme/private/${DATE}.bak/fullchain.pem ${ACME_BASE}/cert.pem diff --git a/data/Dockerfiles/dockerapi/server.py b/data/Dockerfiles/dockerapi/server.py index 17e5e0fd..a161c8a6 100644 --- a/data/Dockerfiles/dockerapi/server.py +++ b/data/Dockerfiles/dockerapi/server.py @@ -86,14 +86,14 @@ class container_post(Resource): elif request.json['cmd'] == 'sieve_list' and request.json['username']: try: for container in docker_client.containers.list(filters={"id": container_id}): - sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve list -u '" + request.json['username'].replace("'", "'\\''") + "'"], user='vmail') + sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve list -u '" + request.json['username'].replace("'", "'\\''") + "'"]) return sieve_return.output except Exception as e: return jsonify(type='danger', msg=str(e)) elif request.json['cmd'] == 'sieve_print' and request.json['script_name'] and request.json['username']: try: for container in docker_client.containers.list(filters={"id": container_id}): - sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"], user='vmail') + sieve_return = container.exec_run(["/bin/bash", "-c", "/usr/local/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"]) return sieve_return.output except Exception as e: return jsonify(type='danger', msg=str(e)) diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index db7ecb8b..d45322b7 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -2,7 +2,7 @@ set -e # Wait for MySQL to warm-up -while ! mysqladmin ping --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do echo "Waiting for database to come up..." sleep 2 done @@ -117,7 +117,7 @@ echo ${RAND_USER}@mailcow.local:$(doveadm pw -s SHA1 -p ${RAND_PASS}) > /usr/loc echo ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/sieve.creds # 401 is user dovecot -if [[ ! -f /mail_crypt/ecprivkey.pem || ! -f /mail_crypt/ecpubkey.pem ]]; then +if [[ ! -s /mail_crypt/ecprivkey.pem || ! -s /mail_crypt/ecpubkey.pem ]]; then openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem openssl pkey -in /mail_crypt/ecprivkey.pem -pubout -out /mail_crypt/ecpubkey.pem chown 401 /mail_crypt/ecprivkey.pem /mail_crypt/ecpubkey.pem @@ -131,6 +131,9 @@ sievec /usr/local/lib/dovecot/sieve/report-spam.sieve sievec /usr/local/lib/dovecot/sieve/report-ham.sieve # Fix permissions +chown root:root /usr/local/etc/dovecot/sql/*.conf +chown root:dovecot /usr/local/etc/dovecot/sql/dovecot-dict-sql-sieve* /usr/local/etc/dovecot/sql/dovecot-dict-sql-quota* +chmod 640 /usr/local/etc/dovecot/sql/*.conf chown -R vmail:vmail /var/vmail/sieve # Fix more than 1 hardlink issue diff --git a/data/Dockerfiles/phpfpm/docker-entrypoint.sh b/data/Dockerfiles/phpfpm/docker-entrypoint.sh index 50c5275d..694d5cf6 100755 --- a/data/Dockerfiles/phpfpm/docker-entrypoint.sh +++ b/data/Dockerfiles/phpfpm/docker-entrypoint.sh @@ -4,7 +4,7 @@ set -e function array_by_comma { local IFS=","; echo "$*"; } # Wait for containers -while ! mysqladmin ping --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do echo "Waiting for SQL..." sleep 2 done @@ -14,6 +14,10 @@ until [[ $(redis-cli -h redis-mailcow PING) == "PONG" ]]; do sleep 2 done +# Trigger db init +echo "Running DB init..." +php -c /usr/local/etc/php -f /web/inc/init_db.inc.php + # Migrate domain map declare -a DOMAIN_ARR redis-cli -h redis-mailcow DEL DOMAIN_MAP @@ -51,9 +55,8 @@ if [[ ${API_ALLOW_FROM} != "invalid" ]] && \ VALIDATED_IPS=$(array_by_comma ${VALIDATED_API_ALLOW_FROM_ARR[*]}) if [[ ! -z ${VALIDATED_IPS} ]]; then mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF -INSERT INTO api (username, api_key, active, allow_from) -SELECT username, "${API_KEY}", '1', "${VALIDATED_IPS}" FROM admin WHERE superadmin='1' AND active='1' -ON DUPLICATE KEY UPDATE active = '1', allow_from = "${VALIDATED_IPS}", api_key = "${API_KEY}"; +DELETE FROM api; +INSERT INTO api (api_key, active, allow_from) VALUES ("${API_KEY}", "1", "${VALIDATED_IPS}"); EOF fi fi diff --git a/data/Dockerfiles/postfix/postfix.sh b/data/Dockerfiles/postfix/postfix.sh index 628322d8..83ec94e0 100755 --- a/data/Dockerfiles/postfix/postfix.sh +++ b/data/Dockerfiles/postfix/postfix.sh @@ -252,6 +252,8 @@ chmod 700 /var/lib/zeyple/keys chown -R 600:600 /var/lib/zeyple/keys # Fix Postfix permissions +chown -R root:postfix /opt/postfix/conf/sql/ +chmod 640 /opt/postfix/conf/sql/*.cf chgrp -R postdrop /var/spool/postfix/public chgrp -R postdrop /var/spool/postfix/maildrop postfix set-permissions diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh index 77f03b63..b8dc895c 100755 --- a/data/Dockerfiles/sogo/bootstrap-sogo.sh +++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh @@ -1,7 +1,7 @@ #!/bin/bash # Wait for MySQL to warm-up -while ! mysqladmin ping --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do echo "Waiting for database to come up..." sleep 2 done diff --git a/data/Dockerfiles/unbound/Dockerfile b/data/Dockerfiles/unbound/Dockerfile index 4f443b88..8e056a93 100644 --- a/data/Dockerfiles/unbound/Dockerfile +++ b/data/Dockerfiles/unbound/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.6 +FROM alpine:3.8 LABEL maintainer "Andre Peters " diff --git a/data/conf/dovecot/dovecot.conf b/data/conf/dovecot/dovecot.conf index 2df625e2..02ce5dff 100644 --- a/data/conf/dovecot/dovecot.conf +++ b/data/conf/dovecot/dovecot.conf @@ -318,4 +318,5 @@ service stats { user = vmail } } +imap_max_line_length = 2 M !include_try /usr/local/etc/dovecot/extra.conf diff --git a/data/conf/phpfpm/php-conf.d/other.ini b/data/conf/phpfpm/php-conf.d/other.ini index 2d05a5b7..4a90901d 100644 --- a/data/conf/phpfpm/php-conf.d/other.ini +++ b/data/conf/phpfpm/php-conf.d/other.ini @@ -1,2 +1,4 @@ session.save_handler = redis session.save_path = "tcp://redis:6379" +max_execution_time = 1200 +max_input_time = 1200 diff --git a/data/conf/phpfpm/php-fpm.d/pools.conf b/data/conf/phpfpm/php-fpm.d/pools.conf index d6520948..c0db1e04 100644 --- a/data/conf/phpfpm/php-fpm.d/pools.conf +++ b/data/conf/phpfpm/php-fpm.d/pools.conf @@ -11,8 +11,6 @@ access.log = /proc/self/fd/2 clear_env = no catch_workers_output = yes php_admin_value[memory_limit] = 256M -php_admin_value[max_execution_time] = 1200 -php_admin_value[max_input_time] = 1200 [web-worker] user = www-data @@ -27,6 +25,3 @@ access.log = /proc/self/fd/2 clear_env = no catch_workers_output = yes php_admin_value[memory_limit] = 512M -php_admin_value[max_execution_time] = 1200 -php_admin_value[max_input_time] = 1200 - diff --git a/data/conf/rspamd/meta_exporter/pipe.php b/data/conf/rspamd/meta_exporter/pipe.php index ffa3a9ca..1e37ab50 100644 --- a/data/conf/rspamd/meta_exporter/pipe.php +++ b/data/conf/rspamd/meta_exporter/pipe.php @@ -17,6 +17,7 @@ try { $pdo = new PDO($dsn, $database_user, $database_pass, $opt); } catch (PDOException $e) { + error_log("QUARANTINE: " . $e); http_response_code(501); exit; } @@ -61,12 +62,11 @@ $symbols = $headers['X-Rspamd-Symbols']; $raw_size = (int)$_SERVER['CONTENT_LENGTH']; try { - if ($max_size = $redis->Get('Q_MAX_SIZE')) { - if (!empty($max_size) && ($max_size * 1048576) < $raw_size) { - error_log(sprintf("Message too large: %d exceeds %d", $raw_size, ($max_size * 1048576))); - http_response_code(505); - exit; - } + $max_size = (int)$redis->Get('Q_MAX_SIZE'); + if (($max_size * 1048576) < $raw_size) { + error_log(sprintf("QUARANTINE: Message too large: %d b exceeds %d b", $raw_size, ($max_size * 1048576))); + http_response_code(505); + exit; } if ($exclude_domains = $redis->Get('Q_EXCLUDE_DOMAINS')) { $exclude_domains = json_decode($exclude_domains, true); @@ -74,7 +74,7 @@ try { $retention_size = (int)$redis->Get('Q_RETENTION_SIZE'); } catch (RedisException $e) { - error_log($e); + error_log("QUARANTINE: " . $e); http_response_code(504); exit; } @@ -93,14 +93,14 @@ foreach (json_decode($rcpts, true) as $rcpt) { } } catch (RedisException $e) { - error_log($e); + error_log("QUARANTINE: " . $e); http_response_code(504); exit; } // Skip if domain is excluded if (in_array($parsed_rcpt['domain'], $exclude_domains)) { - error_log(sprintf("Skipped domain %s", $parsed_rcpt['domain'])); + error_log(sprintf("QUARANTINE: Skipped domain %s", $parsed_rcpt['domain'])); continue; } @@ -135,12 +135,12 @@ foreach (json_decode($rcpts, true) as $rcpt) { // Loop through all found gotos foreach ($gotos_array as $index => &$goto) { - error_log("quarantine pipe: query " . $goto . " as username from mailbox"); + error_log("QUARANTINE: quarantine pipe: query " . $goto . " as username from mailbox"); $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND `active`= '1';"); $stmt->execute(array(':goto' => $goto)); $username = $stmt->fetch(PDO::FETCH_ASSOC)['username']; if (!empty($username)) { - error_log("quarantine pipe: mailbox found: " . $username); + error_log("QUARANTINE: quarantine pipe: mailbox found: " . $username); // Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate if (!in_array($username, $rcpt_final_mailboxes)) { $rcpt_final_mailboxes[] = $username; @@ -149,13 +149,13 @@ foreach (json_decode($rcpts, true) as $rcpt) { else { $parsed_goto = parse_email($goto); if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) { - error_log($goto . " is not a mailcow handled mailbox or alias address"); + error_log("QUARANTINE:" . $goto . " is not a mailcow handled mailbox or alias address"); } else { $stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'"); $stmt->execute(array(':goto' => $goto)); $goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto']; - error_log("quarantine pipe: goto address " . $goto . " is a alias branch for " . $goto_branch); + error_log("QUARANTINE: quarantine pipe: goto address " . $goto . " is a alias branch for " . $goto_branch); $goto_branch_array = explode(',', $goto_branch); } } @@ -175,18 +175,18 @@ foreach (json_decode($rcpts, true) as $rcpt) { // Force exit if loop cannot be solved // Postfix does not allow for alias loops, so this should never happen. $loop_c++; - error_log("quarantine pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array)); + error_log("QUARANTINE: quarantine pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array)); } } catch (PDOException $e) { - error_log($e->getMessage()); + error_log("QUARANTINE: " . $e->getMessage()); http_response_code(502); exit; } } foreach ($rcpt_final_mailboxes as $rcpt) { - error_log("quarantine pipe: processing quarantine message for rcpt " . $rcpt); + error_log("QUARANTINE: quarantine pipe: processing quarantine message for rcpt " . $rcpt); try { $stmt = $pdo->prepare("INSERT INTO `quarantine` (`qid`, `score`, `sender`, `rcpt`, `symbols`, `user`, `ip`, `msg`, `action`) VALUES (:qid, :score, :sender, :rcpt, :symbols, :user, :ip, :msg, :action)"); @@ -218,7 +218,7 @@ foreach ($rcpt_final_mailboxes as $rcpt) { )); } catch (PDOException $e) { - error_log($e->getMessage()); + error_log("QUARANTINE: " . $e->getMessage()); http_response_code(503); exit; } diff --git a/data/conf/unbound/unbound.conf b/data/conf/unbound/unbound.conf index 6d7f1f04..af725f0f 100644 --- a/data/conf/unbound/unbound.conf +++ b/data/conf/unbound/unbound.conf @@ -8,20 +8,22 @@ server: do-udp: yes do-tcp: yes do-daemonize: no + #access-control: 0.0.0.0/0 allow access-control: 10.0.0.0/8 allow access-control: 172.16.0.0/12 allow access-control: 192.168.0.0/16 allow access-control: fc00::/7 allow access-control: fe80::/10 allow + #access-control: ::0/0 allow directory: "/etc/unbound" username: unbound auto-trust-anchor-file: trusted-key.key - private-address: 10.0.0.0/8 - private-address: 172.16.0.0/12 - private-address: 192.168.0.0/16 - private-address: 169.254.0.0/16 - private-address: fc00::/7 - private-address: fe80::/10 + #private-address: 10.0.0.0/8 + #private-address: 172.16.0.0/12 + #private-address: 192.168.0.0/16 + #private-address: 169.254.0.0/16 + #private-address: fc00::/7 + #private-address: fe80::/10 root-hints: "/etc/unbound/root.hints" hide-identity: yes hide-version: yes diff --git a/data/web/admin.php b/data/web/admin.php index 05ae3174..a4d7b697 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -1,8 +1,8 @@ @@ -10,6 +10,7 @@ $tfa_data = get_tfa();
@@ -17,34 +18,28 @@ $tfa_data = get_tfa();
-
- -
- -
- - ↳ a-z A-Z - _ . -
+
+
+
+
+
+ + + +
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
- - +
+ + + +
:
@@ -68,7 +63,7 @@ $tfa_data = get_tfa();
:
- @@ -79,24 +74,27 @@ $tfa_data = get_tfa(); API (experimental, work in progress) +
- +
- +
@@ -117,7 +115,7 @@ $tfa_data = get_tfa();
-
+
@@ -776,6 +774,88 @@ $tfa_data = get_tfa();
+
+
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { + padding: 3px; +} \ No newline at end of file diff --git a/data/web/debug.php b/data/web/debug.php index d6616dcb..d3d7d794 100644 --- a/data/web/debug.php +++ b/data/web/debug.php @@ -1,8 +1,8 @@ diff --git a/data/web/edit.php b/data/web/edit.php index 31bb5fa3..87d563b0 100644 --- a/data/web/edit.php +++ b/data/web/edit.php @@ -1,11 +1,11 @@
@@ -18,106 +18,35 @@ require_once("inc/header.inc.php"); -

-
-
- -
- -
- -
-
-
- -
- -
- -
-
- -
-
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
- - - -

+ if (isset($_GET["alias"]) && + !empty($_GET["alias"])) { + $alias = html_entity_decode(rawurldecode($_GET["alias"])); + $result = mailbox('get', 'alias_details', $alias); + if (!empty($result)) { + ?> +


-
+
- +
- +
- +
- -
-
-
- -
- -
-
-
- -
- + +
+ +
+
+ +
+
+ +
@@ -129,38 +58,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
-
- -
-
-
-
-
- -
-
-
-
-
-
-

ACL

-
-
-
- -
-
- -
+
@@ -172,6 +70,159 @@ if (isset($_SESSION['mailcow_cc_role'])) { +

+
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+

ACL

+
+
+
+ +
+
+ +
+
+
+
+ + + +

+
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ + + diff --git a/data/web/inc/functions.admin.inc.php b/data/web/inc/functions.admin.inc.php new file mode 100644 index 00000000..79c4b9cc --- /dev/null +++ b/data/web/inc/functions.admin.inc.php @@ -0,0 +1,276 @@ + 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'access_denied' + ); + return false; + } + global $pdo; + global $lang; + $_data_log = $_data; + !isset($_data_log['password']) ?: $_data_log['password'] = '*'; + !isset($_data_log['password2']) ?: $_data_log['password2'] = '*'; + switch ($_action) { + case 'add': + $username = strtolower(trim($_data['username'])); + $password = $_data['password']; + $password2 = $_data['password2']; + $active = intval($_data['active']); + if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'username_invalid' + ); + return false; + } + + $stmt = $pdo->prepare("SELECT `username` FROM `admin` + WHERE `username` = :username"); + $stmt->execute(array(':username' => $username)); + $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + + $stmt = $pdo->prepare("SELECT `username` FROM `domain_admins` + WHERE `username` = :username"); + $stmt->execute(array(':username' => $username)); + $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + + foreach ($num_results as $num_results_each) { + if ($num_results_each != 0) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('object_exists', htmlspecialchars($username)) + ); + return false; + } + } + if (!empty($password) && !empty($password2)) { + if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'password_complexity' + ); + return false; + } + if ($password != $password2) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'password_mismatch' + ); + return false; + } + $password_hashed = hash_password($password); + $stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`) + VALUES (:username, :password_hashed, '1', :active)"); + $stmt->execute(array( + ':username' => $username, + ':password_hashed' => $password_hashed, + ':active' => $active + )); + } + else { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'password_empty' + ); + return false; + } + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('admin_added', htmlspecialchars($username)) + ); + break; + case 'edit': + if (!is_array($_data['username'])) { + $usernames = array(); + $usernames[] = $_data['username']; + } + else { + $usernames = $_data['username']; + } + foreach ($usernames as $username) { + $is_now = admin('details', $username); + if (!empty($is_now)) { + $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int']; + $username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username']; + } + else { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'access_denied' + ); + continue; + } + $password = $_data['password']; + $password2 = $_data['password2']; + if ($active == 0) { + $left_active = 0; + foreach (admin('get') as $admin) { + $left_active = $left_active + admin('details', $admin)['active_int']; + } + if ($left_active == 1) { + $_SESSION['return'][] = array( + 'type' => 'warning', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'no_active_admin' + ); + continue; + } + } + if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('username_invalid', $username_new) + ); + continue; + } + if ($username_new != $username) { + if (!empty(admin('details', $username_new)['username'])) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('username_invalid', $username_new) + ); + continue; + } + } + if (!empty($password) && !empty($password2)) { + if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'password_complexity' + ); + continue; + } + if ($password != $password2) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'password_mismatch' + ); + continue; + } + $password_hashed = hash_password($password); + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username"); + $stmt->execute(array( + ':password_hashed' => $password_hashed, + ':username_new' => $username_new, + ':username' => $username, + ':active' => $active + )); + if (isset($_data['disable_tfa'])) { + $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); + $stmt->execute(array(':username' => $username)); + } + else { + $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username"); + $stmt->execute(array(':username_new' => $username_new, ':username' => $username)); + } + } + else { + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username"); + $stmt->execute(array( + ':username_new' => $username_new, + ':username' => $username, + ':active' => $active + )); + if (isset($_data['disable_tfa'])) { + $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); + $stmt->execute(array(':username' => $username)); + } + else { + $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username"); + $stmt->execute(array(':username_new' => $username_new, ':username' => $username)); + } + } + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('admin_modified', htmlspecialchars($username)) + ); + } + return true; + break; + case 'delete': + $usernames = (array)$_data['username']; + foreach ($usernames as $username) { + if ($_SESSION['mailcow_cc_role'] == $username) { + $_SESSION['return'][] = array( + 'type' => 'warning', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'cannot_delete_self' + ); + continue; + } + if (empty(admin('details', $username))) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('username_invalid', $username) + ); + continue; + } + $stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username"); + $stmt->execute(array( + ':username' => $username, + )); + $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username"); + $stmt->execute(array( + ':username' => $username, + )); + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('admin_removed', htmlspecialchars($username)) + ); + } + break; + case 'get': + $admins = array(); + $stmt = $pdo->query("SELECT `username` FROM `admin` WHERE `superadmin` = '1'"); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + while ($row = array_shift($rows)) { + $admins[] = $row['username']; + } + return $admins; + break; + case 'details': + $admindata = array(); + $stmt = $pdo->prepare("SELECT + `tfa`.`active` AS `tfa_active_int`, + CASE `tfa`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `tfa_active`, + `admin`.`username`, + `admin`.`created`, + `admin`.`active` AS `active_int`, + CASE `admin`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active` + FROM `admin` + LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`admin`.`username` + WHERE `admin`.`username`= :admin AND `superadmin` = '1'"); + $stmt->execute(array( + ':admin' => $_data + )); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if (empty($row)) { + return false; + } + $admindata['username'] = $row['username']; + $admindata['tfa_active'] = $row['tfa_active']; + $admindata['active'] = $row['active']; + $admindata['tfa_active_int'] = $row['tfa_active_int']; + $admindata['active_int'] = $row['active_int']; + $admindata['created'] = $row['created']; + return $admindata; + break; + } +} diff --git a/data/web/inc/functions.domain_admin.inc.php b/data/web/inc/functions.domain_admin.inc.php index f71a42a1..bf9900e8 100644 --- a/data/web/inc/functions.domain_admin.inc.php +++ b/data/web/inc/functions.domain_admin.inc.php @@ -1,5 +1,4 @@ execute(array( ':username' => $username, )); + $stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username"); + $stmt->execute(array( + ':username_new' => $username_new, + ':username' => $username + )); if (!empty($domains)) { foreach ($domains as $domain) { $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`) @@ -277,7 +281,6 @@ function domain_admin($_action, $_data = null) { WHERE `username` = :user"); $stmt->execute(array(':user' => $username)); $row = $stmt->fetch(PDO::FETCH_ASSOC); - if (!verify_hash($row['password'], $password_old)) { $_SESSION['return'][] = array( 'type' => 'danger', @@ -286,7 +289,6 @@ function domain_admin($_action, $_data = null) { ); return false; } - if (!empty($password_new2) && !empty($password_new)) { if ($password_new2 != $password_new) { $_SESSION['return'][] = array( @@ -329,7 +331,7 @@ function domain_admin($_action, $_data = null) { } $usernames = (array)$_data['username']; foreach ($usernames as $username) { - if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) { + if (empty(domain_admin('details', $username))) { $_SESSION['return'][] = array( 'type' => 'danger', 'log' => array(__FUNCTION__, $_action, $_data_log), @@ -345,6 +347,10 @@ function domain_admin($_action, $_data = null) { $stmt->execute(array( ':username' => $username, )); + $stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username"); + $stmt->execute(array( + ':username' => $username, + )); $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $_action, $_data_log), @@ -362,7 +368,6 @@ function domain_admin($_action, $_data = null) { ); return false; } - $stmt = $pdo->query("SELECT DISTINCT `username` FROM `domain_admins` @@ -374,23 +379,19 @@ function domain_admin($_action, $_data = null) { while ($row = array_shift($rows)) { $domainadmins[] = $row['username']; } - return $domainadmins; break; case 'details': $domainadmindata = array(); - if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) { return false; } elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) { return false; } - if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) { return false; } - $stmt = $pdo->prepare("SELECT `tfa`.`active` AS `tfa_active_int`, CASE `tfa`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `tfa_active`, @@ -413,7 +414,7 @@ function domain_admin($_action, $_data = null) { $domainadmindata['active'] = $row['active']; $domainadmindata['tfa_active_int'] = $row['tfa_active_int']; $domainadmindata['active_int'] = $row['active_int']; - $domainadmindata['modified'] = $row['created']; + $domainadmindata['created'] = $row['created']; // GET SELECTED $stmt = $pdo->prepare("SELECT `domain` FROM `domain` WHERE `domain` IN ( diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index af93d193..4cc43ee3 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -28,7 +28,99 @@ function flush_memcached() { // Dunno } } - +function sys_mail($_data) { + if ($_SESSION['mailcow_cc_role'] != "admin") { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__), + 'msg' => 'access_denied' + ); + return false; + } + $excludes = $_data['mass_exclude']; + $includes = $_data['mass_include']; + $mailboxes = array(); + $mass_from = $_data['mass_from']; + $mass_text = $_data['mass_text']; + $mass_subject = $_data['mass_subject']; + if (!filter_var($mass_from, FILTER_VALIDATE_EMAIL)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__), + 'msg' => 'from_invalid' + ); + return false; + } + if (empty($mass_subject)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__), + 'msg' => 'subject_empty' + ); + return false; + } + if (empty($mass_text)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__), + 'msg' => 'text_empty' + ); + return false; + } + $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')); + foreach ($domains as $domain) { + foreach (mailbox('get', 'mailboxes', $domain) as $mailbox) { + $mailboxes[] = $mailbox; + } + } + if (!empty($includes)) { + $rcpts = array_intersect($mailboxes, $includes); + } + elseif (!empty($excludes)) { + $rcpts = array_diff($mailboxes, $excludes); + } + else { + $rcpts = $mailboxes; + } + if (!empty($rcpts)) { + ini_set('max_execution_time', 0); + ini_set('max_input_time', 0); + $mail = new PHPMailer; + $mail->Timeout = 10; + $mail->SMTPOptions = array( + 'ssl' => array( + 'verify_peer' => false, + 'verify_peer_name' => false, + 'allow_self_signed' => true + ) + ); + $mail->isSMTP(); + $mail->Host = 'dovecot-mailcow'; + $mail->SMTPAuth = false; + $mail->Port = 24; + $mail->setFrom($mass_from); + $mail->Subject = $mass_subject; + $mail->CharSet ="UTF-8"; + $mail->Body = $mass_text; + $mail->XMailer = 'MooMassMail'; + foreach ($rcpts as $rcpt) { + $mail->AddAddress($rcpt); + if (!$mail->send()) { + $_SESSION['return'][] = array( + 'type' => 'warning', + 'log' => array(__FUNCTION__), + 'msg' => 'Mailer error (RCPT "' . htmlspecialchars($rcpt) . '"): ' . str_replace('https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting', '', $mail->ErrorInfo) + ); + } + $mail->ClearAllRecipients(); + } + } + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__), + 'msg' => 'Mass mail job completed, sent ' . count($rcpts) . ' mails' + ); +} function logger($_data = false) { /* logger() will be called as last function @@ -106,21 +198,35 @@ function hasDomainAccess($username, $role, $domain) { if (empty($domain) || !is_valid_domain_name($domain)) { return false; } - if ($role != 'admin' && $role != 'domainadmin' && $role != 'user') { + if ($role != 'admin' && $role != 'domainadmin') { return false; } - $stmt = $pdo->prepare("SELECT `domain` FROM `domain_admins` - WHERE ( - `active`='1' - AND `username` = :username - AND (`domain` = :domain1 OR `domain` = (SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain2)) - ) - OR 'admin' = :role"); - $stmt->execute(array(':username' => $username, ':domain1' => $domain, ':domain2' => $domain, ':role' => $role)); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if (!empty($num_results)) { - return true; - } + if ($role == 'admin') { + $stmt = $pdo->prepare("SELECT `domain` FROM `domain` + WHERE `domain` = :domain"); + $stmt->execute(array(':domain' => $domain)); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` + WHERE `alias_domain` = :domain"); + $stmt->execute(array(':domain' => $domain)); + $num_results = $num_results + count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if ($num_results != 0) { + return true; + } + } + elseif ($role == 'domainadmin') { + $stmt = $pdo->prepare("SELECT `domain` FROM `domain_admins` + WHERE ( + `active`='1' + AND `username` = :username + AND (`domain` = :domain1 OR `domain` = (SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain2)) + )"); + $stmt->execute(array(':username' => $username, ':domain1' => $domain, ':domain2' => $domain)); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if (!empty($num_results)) { + return true; + } + } return false; } function hasMailboxObjectAccess($username, $role, $object) { @@ -318,6 +424,9 @@ function check_login($user, $pass) { } else { unset($_SESSION['ldelay']); + // Reactivate TFA if it was set to "deactivate TFA for next login" + $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user"); + $stmt->execute(array(':user' => $user)); $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $user, '*'), @@ -407,110 +516,6 @@ function formatBytes($size, $precision = 2) { } return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)]; } -function edit_admin_account($_data) { - global $lang; - global $pdo; - $_data_log = $_data; - !isset($_data_log['admin_pass']) ?: $_data_log['admin_pass'] = '*'; - !isset($_data_log['admin_pass2']) ?: $_data_log['admin_pass2'] = '*'; - if ($_SESSION['mailcow_cc_role'] != "admin") { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => 'access_denied' - ); - return false; - } - $username_now = $_SESSION['mailcow_cc_username']; - $username = $_data['admin_user']; - $password = $_data['admin_pass']; - $password2 = $_data['admin_pass2']; - if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => 'username_invalid' - ); - return false; - } - if (!empty($password) && !empty($password2)) { - if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => 'password_complexity' - ); - return false; - } - if ($password != $password2) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => 'password_mismatch' - ); - return false; - } - $password_hashed = hash_password($password); - try { - $stmt = $pdo->prepare("UPDATE `admin` SET - `password` = :password_hashed, - `username` = :username1 - WHERE `username` = :username2"); - $stmt->execute(array( - ':password_hashed' => $password_hashed, - ':username1' => $username, - ':username2' => $username_now - )); - } - catch (PDOException $e) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => array('mysql_error', $e) - ); - return false; - } - } - else { - try { - $stmt = $pdo->prepare("UPDATE `admin` SET - `username` = :username1 - WHERE `username` = :username2"); - $stmt->execute(array( - ':username1' => $username, - ':username2' => $username_now - )); - } - catch (PDOException $e) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => array('mysql_error', $e) - ); - return false; - } - } - try { - $stmt = $pdo->prepare("UPDATE `domain_admins` SET `domain` = 'ALL', `username` = :username1 WHERE `username` = :username2"); - $stmt->execute(array(':username1' => $username, ':username2' => $username_now)); - $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username1 WHERE `username` = :username2"); - $stmt->execute(array(':username1' => $username, ':username2' => $username_now)); - } - catch (PDOException $e) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => array('mysql_error', $e) - ); - return false; - } - $_SESSION['mailcow_cc_username'] = $username; - $_SESSION['return'][] = array( - 'type' => 'success', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => 'admin_modified' - ); -} function update_sogo_static_view() { global $pdo; global $lang; @@ -1113,6 +1118,11 @@ function admin_api($action, $data = null) { $allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from'])); foreach ($allow_from as $key => $val) { if (!filter_var($val, FILTER_VALIDATE_IP)) { + $_SESSION['return'][] = array( + 'type' => 'warning', + 'log' => array(__FUNCTION__, $data), + 'msg' => array('ip_invalid', htmlspecialchars($allow_from[$key])) + ); unset($allow_from[$key]); continue; } @@ -1133,16 +1143,24 @@ function admin_api($action, $data = null) { strtoupper(bin2hex(random_bytes(3))), strtoupper(bin2hex(random_bytes(3))) )); - $stmt = $pdo->prepare("INSERT INTO `api` (`username`, `api_key`, `active`, `allow_from`) - SELECT `username`, :api_key, :active, :allow_from FROM `admin` WHERE `superadmin`='1' AND `active`='1' - ON DUPLICATE KEY UPDATE `active` = :active_u, `allow_from` = :allow_from_u ;"); - $stmt->execute(array( - ':api_key' => $api_key, - ':active' => $active, - ':active_u' => $active, - ':allow_from' => $allow_from, - ':allow_from_u' => $allow_from - )); + $stmt = $pdo->query("SELECT `api_key` FROM `api`"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if (empty($num_results)) { + $stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `active`, `allow_from`) + VALUES (:api_key, :active, :allow_from);"); + $stmt->execute(array( + ':api_key' => $api_key, + ':active' => $active, + ':allow_from' => $allow_from + )); + } + else { + $stmt = $pdo->prepare("UPDATE `api` SET `active` = :active, `allow_from` = :allow_from ;"); + $stmt->execute(array( + ':active' => $active, + ':allow_from' => $allow_from + )); + } break; case "regen_key": $api_key = implode('-', array( @@ -1152,17 +1170,21 @@ function admin_api($action, $data = null) { strtoupper(bin2hex(random_bytes(3))), strtoupper(bin2hex(random_bytes(3))) )); - $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `username` IN - (SELECT `username` FROM `admin` WHERE `superadmin`='1' AND `active`='1')"); + $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key"); $stmt->execute(array( ':api_key' => $api_key )); break; + case "get": + $stmt = $pdo->query("SELECT * FROM `api`"); + $apidata = $stmt->fetch(PDO::FETCH_ASSOC); + return $apidata; + break; } $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $data), - 'msg' => 'admin_modified' + 'msg' => 'admin_api_modified' ); } function rspamd_ui($action, $data = null) { @@ -1233,21 +1255,6 @@ function rspamd_ui($action, $data = null) { break; } } -function get_admin_details() { - // No parameter to be given, only one admin should exist - global $pdo; - global $lang; - $data = array(); - if ($_SESSION['mailcow_cc_role'] != 'admin') { - return false; - } - $stmt = $pdo->query("SELECT `admin`.`username`, `api`.`active` AS `api_active`, `api`.`api_key`, `api`.`allow_from` FROM `admin` - LEFT OUTER JOIN `api` ON `admin`.`username` = `api`.`username` - WHERE `admin`.`superadmin`='1' - AND `admin`.`active`='1'"); - $data = $stmt->fetch(PDO::FETCH_ASSOC); - return $data; -} function get_u2f_registrations($username) { global $pdo; $sel = $pdo->prepare("SELECT * FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = ? AND `active` = '1'"); diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 8332e090..1a4fa3ca 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -2277,7 +2277,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { return false; } elseif (isset($_data) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { - $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain"); + $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` = :domain"); $stmt->execute(array( ':domain' => $_data, )); @@ -2535,7 +2535,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { return false; } elseif (isset($_data) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) { - $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain"); + $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` = :domain"); $stmt->execute(array( ':domain' => $_data, )); @@ -2680,8 +2680,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { SELECT `domain` from `domain_admins` WHERE (`active`='1' AND `username` = :username)) ) - OR ('admin'= :role) - AND `domain` != 'ALL'"); + OR 'admin'= :role"); $stmt->execute(array( ':username' => $_SESSION['mailcow_cc_username'], ':role' => $_SESSION['mailcow_cc_role'], diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index 8ece2544..f2db0f1a 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -3,7 +3,7 @@ function init_db_schema() { try { global $pdo; - $db_version = "03102018_1502"; + $db_version = "07102018_1502"; $stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); @@ -135,7 +135,6 @@ function init_db_schema() { ), "api" => array( "cols" => array( - "username" => "VARCHAR(255) NOT NULL", "api_key" => "VARCHAR(255) NOT NULL", "allow_from" => "VARCHAR(512) NOT NULL", "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", @@ -144,16 +143,8 @@ function init_db_schema() { ), "keys" => array( "primary" => array( - "" => array("username") + "" => array("api_key") ), - "fkey" => array( - "fk_username_api" => array( - "col" => "username", - "ref" => "admin.username", - "delete" => "CASCADE", - "update" => "CASCADE" - ) - ) ), "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" ), @@ -448,14 +439,6 @@ function init_db_schema() { "keys" => array( "primary" => array( "" => array("username") - ), - "fkey" => array( - "fk_domain_admin_acl" => array( - "col" => "username", - "ref" => "domain_admins.username", - "delete" => "CASCADE", - "update" => "NO ACTION" - ) ) ), "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" @@ -987,22 +970,49 @@ DELIMITER ;'; WHERE `username` = :username"); $stmt->execute(array(':tls_enforce_in' => $tls_options['tls_enforce_in'], ':tls_enforce_out' => $tls_options['tls_enforce_out'], ':username' => $tls_user)); } - $_SESSION['return'][] = array( - 'type' => 'success', - 'log' => array(__FUNCTION__), - 'msg' => 'db_init_complete' - ); - + if (php_sapi_name() == "cli") { + echo "DB initialization completed" . PHP_EOL; + } else { + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__), + 'msg' => 'db_init_complete' + ); + } // Fix ACL $stmt = $pdo->query("INSERT INTO `user_acl` (`username`) SELECT `username` FROM `mailbox` WHERE `kind` = '' AND NOT EXISTS (SELECT `username` FROM `user_acl`);"); $stmt = $pdo->query("INSERT INTO `da_acl` (`username`) SELECT DISTINCT `username` FROM `domain_admins` WHERE `username` != 'admin' AND NOT EXISTS (SELECT `username` FROM `da_acl`);"); + // Fix domain_admins + $stmt = $pdo->query("DELETE FROM `domain_admins` WHERE `domain` = 'ALL';"); } catch (PDOException $e) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__), - 'msg' => array('mysql_error', $e) - ); + if (php_sapi_name() == "cli") { + echo "DB initialization failed: " . print_r($e, true) . PHP_EOL; + } else { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__), + 'msg' => array('mysql_error', $e) + ); + } } } -?> +if (php_sapi_name() == "cli") { + include '/web/inc/vars.inc.php'; + $now = new DateTime(); + $mins = $now->getOffset() / 60; + $sgn = ($mins < 0 ? -1 : 1); + $mins = abs($mins); + $hrs = floor($mins / 60); + $mins -= $hrs * 60; + $offset = sprintf('%+d:%02d', $hrs*$sgn, $mins); + $dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name; + $opt = [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, + PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '" . $offset . "', group_concat_max_len = 3423543543;", + ]; + $pdo = new PDO($dsn, $database_user, $database_pass, $opt); + init_db_schema(); +} diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index 4cb742af..67f72b78 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -139,6 +139,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.customize.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.address_rewriting.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.domain_admin.inc.php'; +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.admin.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.quarantine.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.dkim.inc.php'; diff --git a/data/web/inc/sessions.inc.php b/data/web/inc/sessions.inc.php index a3b81407..ac77e9ae 100644 --- a/data/web/inc/sessions.inc.php +++ b/data/web/inc/sessions.inc.php @@ -28,16 +28,16 @@ if (!isset($_SESSION['SESS_REMOTE_UA'])) { // API if (!empty($_SERVER['HTTP_X_API_KEY'])) { - $stmt = $pdo->prepare("SELECT `username`, `allow_from` FROM `api` WHERE `api_key` = :api_key AND `active` = '1';"); + $stmt = $pdo->prepare("SELECT `allow_from` FROM `api` WHERE `api_key` = :api_key AND `active` = '1';"); $stmt->execute(array( - ':api_key' => preg_replace('/[^A-Z0-9-]/i', '', $_SERVER['HTTP_X_API_KEY']) + ':api_key' => preg_replace('/[^a-zA-Z0-9-]/', '', $_SERVER['HTTP_X_API_KEY']) )); $api_return = $stmt->fetch(PDO::FETCH_ASSOC); if (!empty($api_return['username'])) { $remote = get_remote_ip(false); $allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from'])); if (in_array($remote, $allow_from)) { - $_SESSION['mailcow_cc_username'] = $api_return['username']; + $_SESSION['mailcow_cc_username'] = 'API'; $_SESSION['mailcow_cc_role'] = 'admin'; $_SESSION['mailcow_cc_api'] = true; } @@ -84,7 +84,7 @@ if (isset($_POST["logout"])) { $_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"]; $_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"]; unset($_SESSION["dual-login"]); - header("Location: /mailbox.php"); + header("Location: /mailbox"); exit(); } else { diff --git a/data/web/inc/triggers.inc.php b/data/web/inc/triggers.inc.php index d37c0968..d9d87351 100644 --- a/data/web/inc/triggers.inc.php +++ b/data/web/inc/triggers.inc.php @@ -6,7 +6,7 @@ if (isset($_POST["verify_tfa_login"])) { unset($_SESSION['pending_mailcow_cc_username']); unset($_SESSION['pending_mailcow_cc_role']); unset($_SESSION['pending_tfa_method']); - header("Location: /user.php"); + header("Location: /user"); } } @@ -17,19 +17,19 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { $_SESSION['mailcow_cc_username'] = $login_user; $_SESSION['mailcow_cc_role'] = "admin"; $_SESSION['mailcow_cc_last_login'] = last_login($login_user); - header("Location: /admin.php"); + header("Location: /admin"); } elseif ($as == "domainadmin") { $_SESSION['mailcow_cc_username'] = $login_user; $_SESSION['mailcow_cc_role'] = "domainadmin"; $_SESSION['mailcow_cc_last_login'] = last_login($login_user); - header("Location: /mailbox.php"); + header("Location: /mailbox"); } elseif ($as == "user") { $_SESSION['mailcow_cc_username'] = $login_user; $_SESSION['mailcow_cc_role'] = "user"; $_SESSION['mailcow_cc_last_login'] = last_login($login_user); - header("Location: /user.php"); + header("Location: /user"); } elseif ($as != "pending") { unset($_SESSION['pending_mailcow_cc_username']); @@ -49,7 +49,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['acl']['login_as'] == "1") $_SESSION["dual-login"]["role"] = $_SESSION['mailcow_cc_role']; $_SESSION['mailcow_cc_username'] = $duallogin; $_SESSION['mailcow_cc_role'] = "user"; - header("Location: /user.php"); + header("Location: /user"); } } else { @@ -58,7 +58,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['acl']['login_as'] == "1") $_SESSION["dual-login"]["role"] = $_SESSION['mailcow_cc_role']; $_SESSION['mailcow_cc_username'] = $duallogin; $_SESSION['mailcow_cc_role'] = "domainadmin"; - header("Location: /user.php"); + header("Location: /user"); } } } @@ -93,5 +93,8 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi if (isset($_POST["rspamd_ui"])) { rspamd_ui('edit', $_POST); } + if (isset($_POST["mass_send"])) { + sys_mail($_POST); + } } ?> diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index 5cf2ea94..9cb4c0da 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -87,7 +87,12 @@ $AVAILABLE_LANGUAGES = array('de', 'en', 'es', 'fr', 'lv', 'nl', 'pl', 'pt', 'ru $DEFAULT_THEME = 'lumen'; // Password complexity as regular expression -$PASSWD_REGEP = '.{4,}'; +// Min. 6 characters +//$PASSWD_REGEP = '.{6,}'; +// Min. 6 characters, which must include at least one uppercase letter, one lowercase letter and one number +// $PASSWD_REGEP = '^(?=.*[A-Z])(?=.*[0-9])(?=.*[a-z]).{6,}$'; +// Min. 6 characters, which must include at least one letter and one number +$PASSWD_REGEP = '^(?=.*[0-9])(?=.*[A-Za-z]).{6,}$'; // Show DKIM private keys - false by default $SHOW_DKIM_PRIV_KEYS = false; diff --git a/data/web/index.php b/data/web/index.php index c839ea86..fcb35de9 100644 --- a/data/web/index.php +++ b/data/web/index.php @@ -1,5 +1,5 @@ @@ -109,4 +110,4 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
"); item.chkbox = ''; item.action = '
' + ' ' + lang.edit + '' + @@ -149,11 +185,25 @@ jQuery(function($){ ' Login' + '
'; }); + } else if (table == 'adminstable') { + $.each(data, function (i, item) { + if (admin_username == item.username) { + item.usr = '→ ' + item.username; + } else { + item.usr = item.username; + } + item.chkbox = ''; + item.action = ''; + }); } return data }; // Initial table drawings draw_domain_admins(); + draw_admins(); draw_fwd_hosts(); draw_relayhosts(); // Relayhost diff --git a/data/web/js/api.js b/data/web/js/api.js index e8293dbc..1a5f4b06 100644 --- a/data/web/js/api.js +++ b/data/web/js/api.js @@ -81,6 +81,11 @@ $(document).ready(function() { } else { api_reload_window = true; } + if (typeof $(this).data('api-reload-location') !== 'undefined') { + api_reload_location = $(this).data('api-reload-location'); + } else { + api_reload_location = '#'; + } // If clicked element #edit_selected is in a form with the same data-id as the button, // we merge all input fields by {"name":"value"} into api-attr if ($(this).closest("form").data('id') == id) { @@ -151,7 +156,11 @@ $(document).ready(function() { response_obj = JSON.parse(response); } if (api_reload_window === true) { - window.location = window.location.href.split("#")[0]; + if (api_reload_location != '#') { + window.location.replace(api_reload_location) + } else { + window.location = window.location.href.split("#")[0]; + } } } }); diff --git a/data/web/js/debug.js b/data/web/js/debug.js index bf087167..dfd88d86 100644 --- a/data/web/js/debug.js +++ b/data/web/js/debug.js @@ -513,7 +513,13 @@ jQuery(function($){ } else if (table == 'general_syslog') { $.each(data, function (i, item) { if (item === null) { return true; } - item.message = escapeHtml(item.message); + if (item.message.match("^base64,")) { + item.message = atob(item.message.slice(7)); + item.message = item.message.replace(/(?!^)acme-client:/g, '
acme-client:') + item.message = item.message.replace(/acme-client:/g, 'acme-client:') + } else { + item.message = escapeHtml(item.message); + } var danger_class = ["emerg", "alert", "crit", "err"]; var warning_class = ["warning", "warn"]; var info_class = ["notice", "info", "debug"]; diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index 8894e995..ae99ad3d 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -8,6 +8,7 @@ $(document).ready(function() { dataType: 'json', url: '/api/v1/get/domain/all', jsonp: false, + async: false, error: function () { domain_list.push('Cannot read domain list'); }, diff --git a/data/web/json_api.php b/data/web/json_api.php index d1f6a979..e8ca78cc 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -144,6 +144,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u case "domain-admin": process_add_return(domain_admin('add', $attr)); break; + case "admin": + process_add_return(admin('add', $attr)); + break; case "syncjob": process_add_return(mailbox('add', 'syncjob', $attr)); break; @@ -857,6 +860,31 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u break; } break; + case "admin": + switch ($object) { + case "all": + $admins = admin('get'); + if (!empty($admins)) { + foreach ($admins as $admin) { + if ($details = admin('details', $admin)) { + $data[] = $details; + } + else { + continue; + } + } + process_get_return($data); + } + else { + echo '{}'; + } + break; + + default: + process_get_return(admin('details', $object)); + break; + } + break; case "u2f-registration": header('Content-Type: application/javascript'); if (($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") && $_SESSION["mailcow_cc_username"] == $object) { @@ -893,6 +921,14 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u return; } break; + case "dkim": + switch ($object) { + default: + $data = dkim('details', $object); + process_get_return($data); + break; + } + break; default: echo '{}'; break; @@ -984,6 +1020,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u case "domain-admin": process_delete_return(domain_admin('delete', array('username' => $items))); break; + case "admin": + process_delete_return(admin('delete', array('username' => $items))); + break; } break; case "edit": @@ -1088,6 +1127,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u case "domain-admin": process_edit_return(domain_admin('edit', array_merge(array('username' => $items), $attr))); break; + case "admin": + process_edit_return(admin('edit', array_merge(array('username' => $items), $attr))); + break; case "fwdhost": process_edit_return(fwdhost('edit', array_merge(array('fwdhost' => $items), $attr))); break; @@ -1104,9 +1146,6 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u elseif ($_SESSION['mailcow_cc_role'] == "user") { process_edit_return(edit_user_account($attr)); } - elseif ($_SESSION['mailcow_cc_role'] == "admin") { - process_edit_return(edit_admin_account($attr)); - } break; } break; diff --git a/data/web/lang/lang.ca.php b/data/web/lang/lang.ca.php index 32a10dad..91072ecf 100644 --- a/data/web/lang/lang.ca.php +++ b/data/web/lang/lang.ca.php @@ -204,8 +204,6 @@ $lang['mailbox']['target_address'] = 'Direcció Goto'; $lang['mailbox']['username'] = "Nom d'usuari"; $lang['mailbox']['fname'] = 'Nom complert'; $lang['mailbox']['filter_table'] = 'Filtrar taula'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; $lang['mailbox']['in_use'] = 'En ús (%)'; $lang['mailbox']['msg_num'] = 'Missatge #'; $lang['mailbox']['remove'] = 'Esborrar'; @@ -406,8 +404,6 @@ $lang['admin']['save'] = 'Desar els canvis'; $lang['admin']['admin'] = 'Administrador'; $lang['admin']['admin_details'] = "Editar detalls de l'administrador"; $lang['admin']['unchanged_if_empty'] = "Si no hi ha canvis, deixa'l en blanc"; -$lang['admin']['yes'] = '✔'; -$lang['admin']['no'] = '✘'; $lang['admin']['access'] = 'Accés'; $lang['admin']['no_record'] = 'Cap registre'; $lang['admin']['filter_table'] = 'Filtrar taula'; diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index 9392f346..a4a57c14 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -72,7 +72,7 @@ $lang['success']['dkim_removed'] = 'DKIM-Key wurde entfernt'; $lang['success']['dkim_added'] = 'DKIM-Key wurde hinzugefügt'; $lang['success']['dkim_duplicated'] = "DKIM-Key der Domain %s wurde auf Domain %s kopiert"; $lang['danger']['access_denied'] = 'Zugriff verweigert oder unvollständige/ungültige Daten'; -$lang['danger']['domain_invalid'] = 'Domainname %s ist ungültig'; +$lang['danger']['domain_invalid'] = 'Domainname ist leer oder ungültig'; $lang['danger']['mailbox_quota_exceeds_domain_quota'] = 'Maximale Größe für Mailboxen überschreitet das Domain Speicherlimit'; $lang['danger']['object_is_not_numeric'] = 'Wert %s ist nicht numerisch'; $lang['success']['domain_added'] = 'Domain %s wurde angelegt'; @@ -105,7 +105,9 @@ $lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden ges $lang['success']['domain_modified'] = 'Änderungen an Domain %s wurden gespeichert'; $lang['success']['domain_admin_modified'] = 'Änderungen an Domain-Administrator %s wurden gespeichert'; $lang['success']['domain_admin_added'] = 'Domain-Administrator %s wurde angelegt'; +$lang['success']['admin_added'] = 'Administrator %s wurde angelegt'; $lang['success']['admin_modified'] = 'Änderungen am Administrator wurden gespeichert'; +$lang['success']['admin_api_modified'] = "Änderungen an API wurden gespeichert"; $lang['danger']['username_invalid'] = 'Benutzername %s kann nicht verwendet werden'; $lang['danger']['password_mismatch'] = 'Passwort-Wiederholung stimmt nicht überein'; $lang['danger']['password_complexity'] = 'Passwort entspricht nicht den Richtlinien'; @@ -129,9 +131,12 @@ $lang['success']['domain_removed'] = 'Domain %s wurde entfernt'; $lang['success']['alias_removed'] = 'Alias-Adresse %s wurde entfernt'; $lang['success']['alias_domain_removed'] = 'Alias-Domain %s wurde entfernt'; $lang['success']['domain_admin_removed'] = 'Domain-Administrator %s wurde entfernt'; +$lang['success']['admin_removed'] = 'Administrator %s wurde entfernt'; $lang['success']['mailbox_removed'] = 'Mailbox %s wurde entfernt'; $lang['success']['eas_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt"; $lang['success']['resource_removed'] = 'Ressource %s wurde entfernt'; +$lang['warning']['cannot_delete_self'] = 'Kann derzeit eingeloggten Benutzer nicht entfernen'; +$lang['warning']['no_active_admin'] = 'Kann letzten aktiven Administrator nicht deaktivieren'; $lang['danger']['max_quota_in_use'] = 'Mailbox Speicherplatzlimit muss größer oder gleich %d MiB sein'; $lang['danger']['domain_quota_m_in_use'] = 'Domain Speicherplatzlimit muss größer oder gleich %d MiB sein'; $lang['danger']['mailboxes_in_use'] = 'Maximale Anzahl an Mailboxen muss größer oder gleich %d sein'; @@ -279,8 +284,6 @@ $lang['mailbox']['target_address'] = 'Ziel-Adresse'; $lang['mailbox']['username'] = 'Benutzername'; $lang['mailbox']['fname'] = 'Name'; $lang['mailbox']['filter_table'] = 'Filtern'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; $lang['mailbox']['in_use'] = 'Prozentualer Gebrauch'; $lang['mailbox']['msg_num'] = 'Anzahl Nachrichten'; $lang['mailbox']['remove'] = 'Entfernen'; @@ -505,6 +508,7 @@ $lang['admin']['active'] = 'Aktiv'; $lang['admin']['inactive'] = 'Inaktiv'; $lang['admin']['action'] = 'Aktion'; $lang['admin']['add_domain_admin'] = 'Domain-Administrator hinzufügen'; +$lang['admin']['domain_admin'] = 'Administrator hinzufügen'; $lang['admin']['add_settings_rule'] = 'Rspamd Regel hinzufügen'; $lang['admin']['rsetting_desc'] = 'Kurze Beschreibung'; $lang['admin']['rsetting_content'] = 'Regelinhalt'; @@ -523,8 +527,6 @@ $lang['admin']['save'] = 'Änderungen speichern'; $lang['admin']['admin'] = 'Administrator'; $lang['admin']['admin_details'] = 'Administrator bearbeiten'; $lang['admin']['unchanged_if_empty'] = 'Unverändert, wenn leer'; -$lang['admin']['yes'] = '✔'; -$lang['admin']['no'] = '✘'; $lang['admin']['access'] = 'Zugang'; $lang['admin']['no_record'] = 'Kein Eintrag'; $lang['admin']['filter_table'] = 'Tabelle Filtern'; @@ -687,3 +689,19 @@ $lang['success']['tls_policy_map_entry_saved'] = 'TLS-Richtlinieneintrag "%s" wu $lang['success']['tls_policy_map_entry_deleted'] = 'TLS-Richtlinie mit der ID %s wurde gelöscht'; $lang['mailbox']['add_tls_policy_map'] = "TLS-Richtlinieneintrag hinzufügen"; $lang['danger']['tls_policy_map_parameter_invalid'] = "Parameter ist ungültig"; + +$lang['admin']['sys_mails'] = 'System E-Mails'; +$lang['admin']['subject'] = 'Betreff'; +$lang['admin']['from'] = 'Absender'; +$lang['admin']['include_exclude'] = 'Ein- und Ausschlüsse'; +$lang['admin']['include_exclude_info'] = 'Ohne Auswahl werden alle Mailboxen adressiert.'; +$lang['admin']['excludes'] = 'Diese Empfänger ausschließen'; +$lang['admin']['includes'] = 'Diese Empfänger einschließen'; +$lang['admin']['text'] = 'Text'; +$lang['admin']['activate_send'] = 'Senden-Button freischalten'; +$lang['admin']['send'] = 'Senden'; + +$lang['warning']['ip_invalid'] = 'Ungültige IP übersprungen: %s'; +$lang['danger']['text_empty'] = 'Text darf nicht leer sein'; +$lang['danger']['subject_empty'] = 'Betreff darf nicht leer sein'; +$lang['danger']['from_invalid'] = 'From address must be a valid email address'; diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index 195bceb2..90d588ac 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -76,7 +76,7 @@ $lang['success']['dkim_removed'] = "DKIM key %s has been removed"; $lang['success']['dkim_added'] = "DKIM key %s has been saved"; $lang['success']['dkim_duplicated'] = "DKIM key for domain %s has been copied to %s"; $lang['danger']['access_denied'] = "Access denied or invalid form data"; -$lang['danger']['domain_invalid'] = "Domain name %s is invalid"; +$lang['danger']['domain_invalid'] = "Domain name is empty or invalid"; $lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. quota exceeds domain quota limit"; $lang['danger']['object_is_not_numeric'] = "Value %s is not numeric"; $lang['success']['domain_added'] = "Added domain %s"; @@ -108,7 +108,9 @@ $lang['success']['aliasd_modified'] = "Changes to alias domain %s have been save $lang['success']['domain_modified'] = "Changes to domain %s have been saved"; $lang['success']['domain_admin_modified'] = "Changes to domain administrator %s have been saved"; $lang['success']['domain_admin_added'] = "Domain administrator %s has been added"; +$lang['success']['admin_added'] = "Administrator %s has been added"; $lang['success']['admin_modified'] = "Changes to administrator have been saved"; +$lang['success']['admin_api_modified'] = "Changes to API have been saved"; $lang['danger']['username_invalid'] = "Username %s cannot be used"; $lang['danger']['password_mismatch'] = "Confirmation password does not match"; $lang['danger']['password_complexity'] = "Password does not meet the policy"; @@ -132,9 +134,12 @@ $lang['success']['domain_removed'] = "Domain %s has been removed"; $lang['success']['alias_removed'] = "Alias %s has been removed"; $lang['success']['alias_domain_removed'] = "Alias domain %s has been removed"; $lang['success']['domain_admin_removed'] = "Domain administrator %s has been removed"; +$lang['success']['admin_removed'] = "Administrator %s has been removed"; $lang['success']['mailbox_removed'] = "Mailbox %s has been removed"; $lang['success']['eas_reset'] = "ActiveSync devices for user %s were reset"; $lang['success']['resource_removed'] = "Resource %s has been removed"; +$lang['warning']['cannot_delete_self'] = "Cannot delete logged in user"; +$lang['warning']['no_active_admin'] = "Cannot deactivate last active admin"; $lang['danger']['max_quota_in_use'] = "Mailbox quota must be greater or equal to %d MiB"; $lang['danger']['domain_quota_m_in_use'] = "Domain quota must be greater or equal to %s MiB"; $lang['danger']['mailboxes_in_use'] = "Max. mailboxes must be greater or equal to %d"; @@ -281,8 +286,8 @@ $lang['mailbox']['target_address'] = 'Goto address'; $lang['mailbox']['username'] = 'Username'; $lang['mailbox']['fname'] = 'Full name'; $lang['mailbox']['filter_table'] = 'Filter table'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; +$lang['mailbox']['yes'] = '✓'; +$lang['mailbox']['no'] = '✕'; $lang['mailbox']['in_use'] = 'In use (%)'; $lang['mailbox']['msg_num'] = 'Message #'; $lang['mailbox']['remove'] = 'Remove'; @@ -517,6 +522,7 @@ $lang['admin']['active'] = 'Active'; $lang['admin']['inactive'] = 'Inactive'; $lang['admin']['action'] = 'Action'; $lang['admin']['add_domain_admin'] = 'Add domain administrator'; +$lang['admin']['add_admin'] = 'Add administrator'; $lang['admin']['add_settings_rule'] = 'Add settings rule'; $lang['admin']['rsetting_desc'] = 'Short description'; $lang['admin']['rsetting_content'] = 'Rule content'; @@ -535,8 +541,8 @@ $lang['admin']['save'] = 'Save changes'; $lang['admin']['admin'] = 'Administrator'; $lang['admin']['admin_details'] = 'Edit administrator details'; $lang['admin']['unchanged_if_empty'] = 'If unchanged leave blank'; -$lang['admin']['yes'] = '✔'; -$lang['admin']['no'] = '✘'; +$lang['admin']['yes'] = '✓'; +$lang['admin']['no'] = '✕'; $lang['admin']['access'] = 'Access'; $lang['admin']['no_record'] = 'No record'; $lang['admin']['filter_table'] = 'Filter table'; @@ -571,7 +577,7 @@ $lang['diagnostics']['cname_from_a'] = 'Value derived from A/AAAA record. This i $lang['admin']['relay_from'] = '"From:" address'; $lang['admin']['relay_run'] = "Run test"; -$lang['admin']['api_allow_from'] = "Allow API access from these IPs"; +$lang['admin']['api_allow_from'] = "Allow API access from these IPs (separated by comma or new line)"; $lang['admin']['api_key'] = "API key"; $lang['admin']['activate_api'] = "Activate API"; $lang['admin']['regen_api_key'] = "Regenerate API key"; @@ -707,3 +713,19 @@ $lang['oauth2']['permit'] = 'Authorize application'; $lang['oauth2']['authorize_app'] = 'Authorize application'; $lang['oauth2']['deny'] = 'Deny'; $lang['oauth2']['access_denied'] = 'Please login as mailbox owner to grant access via OAuth2.'; + +$lang['admin']['sys_mails'] = 'System mails'; +$lang['admin']['subject'] = 'Subject'; +$lang['admin']['from'] = 'From'; +$lang['admin']['include_exclude'] = 'Include/Exclude'; +$lang['admin']['include_exclude_info'] = 'By default - with no selection - all mailboxes are addressed'; +$lang['admin']['excludes'] = 'Excludes these recipients'; +$lang['admin']['includes'] = 'Include these recipients'; +$lang['admin']['text'] = 'Text'; +$lang['admin']['activate_send'] = 'Activate send button'; +$lang['admin']['send'] = 'Send'; + +$lang['warning']['ip_invalid'] = 'Skipped invalid IP: %s'; +$lang['danger']['text_empty'] = 'Text must not be empty'; +$lang['danger']['subject_empty'] = 'Subject must not be empty'; +$lang['danger']['from_invalid'] = 'Absender darf nicht leer sein'; diff --git a/data/web/lang/lang.es.php b/data/web/lang/lang.es.php index 570cea20..0d0e033b 100644 --- a/data/web/lang/lang.es.php +++ b/data/web/lang/lang.es.php @@ -153,8 +153,6 @@ $lang['mailbox']['target_address'] = 'Dirección Goto'; $lang['mailbox']['username'] = 'Nombre de usuario'; $lang['mailbox']['fname'] = 'Nombre completo'; $lang['mailbox']['filter_table'] = 'Filtrar tabla'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; $lang['mailbox']['in_use'] = 'En uso (%)'; $lang['mailbox']['msg_num'] = 'Mensaje #'; $lang['mailbox']['remove'] = 'Eliminar'; @@ -259,7 +257,5 @@ $lang['admin']['save'] = 'Guardar cambios'; $lang['admin']['admin'] = 'Administrador'; $lang['admin']['admin_details'] = 'Editar detalles del administrador'; $lang['admin']['unchanged_if_empty'] = 'Si no hay cambios dejalo en blanco'; -$lang['admin']['yes'] = '✔'; -$lang['admin']['no'] = '✘'; $lang['admin']['access'] = 'Acceso'; $lang['admin']['no_record'] = 'Sin registro'; diff --git a/data/web/lang/lang.fr.php b/data/web/lang/lang.fr.php index a67a447f..6d40a02e 100644 --- a/data/web/lang/lang.fr.php +++ b/data/web/lang/lang.fr.php @@ -188,8 +188,6 @@ $lang['mailbox']['target_address'] = "Adresse cible"; $lang['mailbox']['username'] = "Identifiant"; $lang['mailbox']['fname'] = "Nom complet"; $lang['mailbox']['filter_table'] = "Table de filtrage"; -$lang['mailbox']['yes'] = "✔"; -$lang['mailbox']['no'] = "✘"; $lang['mailbox']['in_use'] = "Utilisation (%)"; $lang['mailbox']['msg_num'] = "Message"; $lang['mailbox']['remove'] = "Retirer"; @@ -358,8 +356,6 @@ $lang['admin']['remove'] = "Retirer"; $lang['admin']['admin'] = "Administrateur"; $lang['admin']['admin_details'] = "Éditer les informations de l'administrateur"; $lang['admin']['unchanged_if_empty'] = "Si aucun changement, laisser vide"; -$lang['admin']['yes'] = "✔"; -$lang['admin']['no'] = "✘"; $lang['admin']['access'] = "Accès"; $lang['admin']['no_record'] = "Aucun enregistrement"; $lang['admin']['filter_table'] = "Table de filtrage"; diff --git a/data/web/lang/lang.it.php b/data/web/lang/lang.it.php index da355609..b79fcb59 100644 --- a/data/web/lang/lang.it.php +++ b/data/web/lang/lang.it.php @@ -191,8 +191,6 @@ $lang['mailbox']['target_address'] = 'Vai ad indirizzo'; $lang['mailbox']['username'] = 'Nome utente'; $lang['mailbox']['fname'] = 'Nome completo'; $lang['mailbox']['filter_table'] = 'Filra tabella'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; $lang['mailbox']['in_use'] = 'In uso (%)'; $lang['mailbox']['msg_num'] = 'Messaggio #'; $lang['mailbox']['remove'] = 'Rimuovi'; @@ -348,8 +346,6 @@ $lang['admin']['save'] = 'Salva modifiche'; $lang['admin']['admin'] = 'Amministratore'; $lang['admin']['admin_details'] = 'Modifica impostazioni amministratore'; $lang['admin']['unchanged_if_empty'] = 'Se immutato lasciare vuoto'; -$lang['admin']['yes'] = '✔'; -$lang['admin']['no'] = '✘'; $lang['admin']['access'] = 'Accedi'; $lang['admin']['no_record'] = 'Nessun risultato'; $lang['admin']['filter_table'] = 'Tabella filtro'; diff --git a/data/web/lang/lang.lv.php b/data/web/lang/lang.lv.php index f612607b..2fe3290e 100644 --- a/data/web/lang/lang.lv.php +++ b/data/web/lang/lang.lv.php @@ -206,8 +206,6 @@ $lang['mailbox']['target_address'] = 'Doties uz adresi'; $lang['mailbox']['username'] = 'Lietotājvārds'; $lang['mailbox']['fname'] = 'Pilns vārds'; $lang['mailbox']['filter_table'] = 'Filtra tabula'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; $lang['mailbox']['in_use'] = 'Lietošanā (%)'; $lang['mailbox']['msg_num'] = 'Vēstule #'; $lang['mailbox']['remove'] = 'Noņemt'; @@ -404,8 +402,6 @@ $lang['admin']['save'] = 'Saglabāt izmaiņas'; $lang['admin']['admin'] = 'Administrators'; $lang['admin']['admin_details'] = 'Labot administratora detaļas'; $lang['admin']['unchanged_if_empty'] = 'Ja nav veiktas izmaiņas, atstājiet tukšu'; -$lang['admin']['yes'] = '✔'; -$lang['admin']['no'] = '✘'; $lang['admin']['access'] = 'Pieeja'; $lang['admin']['no_record'] = 'Nav ierakstu'; $lang['admin']['filter_table'] = 'Filtru tabula'; diff --git a/data/web/lang/lang.nl.php b/data/web/lang/lang.nl.php index 3f87089b..26d16442 100644 --- a/data/web/lang/lang.nl.php +++ b/data/web/lang/lang.nl.php @@ -16,6 +16,9 @@ $lang['footer']['delete_these_items'] = 'Bevestig de wijzigingen aan het volgend $lang['footer']['delete_now'] = 'Nu verwijderen'; $lang['footer']['cancel'] = 'Annuleren'; +$lang['footer']['hibp_nok'] = 'Dit is een potentieel onveilig wachtwoord!'; +$lang['footer']['hibp_ok'] = 'Dit wachtwoord is relatief veilig'; + $lang['danger']['mysql_error'] = "MySQL-fout: %s"; $lang['danger']['redis_error'] = "Redis-fout: %s"; $lang['danger']['unknown_tfa_method'] = "Onbekende tweefactorauthenticatiemethode"; @@ -41,7 +44,9 @@ $lang['danger']['value_missing'] = "Niet alle waarden zijn ingevuld"; $lang['danger']['filter_type'] = "Verkeerd filtertype"; $lang['danger']['domain_cannot_match_hostname'] = "Domein kan niet gelijk zijn aan hostname"; $lang['warning']['domain_added_sogo_failed'] = "Domein is toegevoegd, maar het hestarten van SOGo mislukte. Controleer de serverlogs."; -$lang['danger']['rl_timeframe'] = "Ratelimit time frame is incorrect"; +$lang['danger']['rl_timeframe'] = "Ratelimit-tijdsbestek is ongeldig"; +$lang['success']['rl_saved'] = "Ratelimit voor object %s is opgeslagen"; +$lang['success']['acl_saved'] = "ACL voor object %s is opgeslagen"; $lang['success']['deleted_syncjobs'] = "Synchronisatietaken %s zijn verwijderd"; $lang['success']['deleted_syncjob'] = "Synchronisatietaak %s is verwijderd"; $lang['success']['delete_filters'] = "Filters %s zijn verwijderd"; @@ -68,13 +73,14 @@ $lang['warning']['session_token'] = "Token ongeldig: komt niet overeen"; $lang['danger']['dkim_domain_or_sel_invalid'] = "DKIM-domein %s ongeldig"; $lang['success']['dkim_removed'] = "DKIM-sleutel %s is verwijderd"; -$lang['success']['dkim_added'] = "DKIM-sleutel is opgeslagen"; +$lang['success']['dkim_added'] = "DKIM-sleutel %s is opgeslagen"; +$lang['success']['dkim_duplicated'] = "DKIM-sleutel voor domein %s is gekopieerd naar %s"; $lang['danger']['access_denied'] = "Toegang geweigerd of ongeldige gegevens"; $lang['danger']['domain_invalid'] = "Domeinnaam %s is ongeldig"; $lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. postvakquotum is groter dan domeinquotum"; $lang['danger']['object_is_not_numeric'] = "Waarde %s is niet numeriek"; $lang['success']['domain_added'] = "Domein %s is toegevoegd"; -$lang['success']['items_deleted'] = "Onderdeel %s is verwijderd"; +$lang['success']['items_deleted'] = "Onderdelen %s zijn verwijderd"; $lang['success']['item_deleted'] = "Onderdeel %s is verwijderd"; $lang['danger']['alias_empty'] = "Aliasadres moet ingevuld worden"; $lang['danger']['last_key'] = 'De laatste sleutel kan niet worden verwijderd'; @@ -247,6 +253,7 @@ $lang['mailbox']['description'] = 'Beschrijving'; $lang['mailbox']['alias'] = 'Alias'; $lang['mailbox']['aliases'] = 'Aliassen'; $lang['mailbox']['domains'] = 'Domeinen'; +$lang['admin']['domain_s'] = 'Domein(en)'; $lang['mailbox']['mailboxes'] = 'Postvakken'; $lang['mailbox']['resources'] = 'Hulpbronnen'; $lang['mailbox']['mailbox_quota'] = 'Max. grootte van een postvak'; @@ -260,8 +267,6 @@ $lang['mailbox']['target_address'] = 'Doeladres'; $lang['mailbox']['username'] = 'Gebruikersnaam'; $lang['mailbox']['fname'] = 'Volledige naam'; $lang['mailbox']['filter_table'] = 'Filtertabel'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; $lang['mailbox']['in_use'] = 'In gebruik (%)'; $lang['mailbox']['msg_num'] = 'Bericht #'; $lang['mailbox']['remove'] = 'Verwijder'; @@ -333,6 +338,7 @@ $lang['edit']['full_name'] = 'Volledige naam'; $lang['edit']['quota_mb'] = 'Quotum (MiB)'; $lang['edit']['sender_acl'] = 'Sta toe om te verzenden als'; $lang['edit']['sender_acl_disabled'] = '↳ Verzendcontrole is uitgeschakeld'; +$lang['user']['sender_acl_disabled'] = '↳ Verzendcontrole is uitgeschakeld'; $lang['edit']['previous'] = 'Vorige pagina'; $lang['edit']['unchanged_if_empty'] = 'Laat leeg wanneer onveranderd'; $lang['edit']['dont_check_sender_acl'] = "Schakel verzendcontrole uit voor domein %s (inclusief aliasdomeinen)"; @@ -340,6 +346,22 @@ $lang['edit']['multiple_bookings'] = 'Meerdere boekingen'; $lang['edit']['kind'] = 'Soort'; $lang['edit']['resource'] = 'Hulpbron'; +$lang['acl']['spam_alias'] = 'Tijdelijke aliassen'; +$lang['acl']['tls_policy'] = 'Versleutelingsbeleid'; +$lang['acl']['spam_score'] = 'Spamscore'; +$lang['acl']['spam_policy'] = 'Blacklist/Whitelist'; +$lang['acl']['delimiter_action'] = 'Delimiter-actie'; +$lang['acl']['syncjobs'] = 'Synchronisatietaken'; +$lang['acl']['eas_reset'] = 'Herstel ActiveSync-apparaatcache'; +$lang['acl']['quarantine'] = 'Quarantaine'; +$lang['acl']['login_as'] = 'Log in als postvakgebruiker'; +$lang['acl']['bcc_maps'] = 'BCC-kaarten'; +$lang['acl']['filters'] = 'Filters'; +$lang['acl']['ratelimit'] = 'Ratelimit'; +$lang['acl']['recipient_maps'] = 'Ontvanger-kaarten'; +$lang['acl']['prohibited'] = 'Geweigerd door ACL'; + +$lang['add']['generate'] = 'genereer'; $lang['add']['syncjob'] = 'Voeg een nieuwe synchronisatietaak toe'; $lang['add']['syncjob_hint'] = 'Wees ervan bewust dat wachtwoorden onversleuteld moeten worden opgeslagen.'; $lang['add']['hostname'] = 'Hostname'; @@ -437,7 +459,13 @@ $lang['admin']['no_new_rows'] = 'Er zijn geen extra rijen beschikbaar'; $lang['admin']['additional_rows'] = ' extra rijen zijn toegevoegd'; // parses to 'n additional rows were added' $lang['admin']['private_key'] = 'Privésleutel'; $lang['admin']['import'] = 'Importeer'; +$lang['admin']['duplicate'] = 'Dupliceer'; $lang['admin']['import_private_key'] = 'Importeer privésleutel'; +$lang['admin']['duplicate_dkim'] = 'Dupliceer DKIM-sleutel'; +$lang['admin']['dkim_from'] = 'Van'; +$lang['admin']['dkim_to'] = 'Naar'; +$lang['admin']['dkim_from_title'] = 'Kopieer data van domein'; +$lang['admin']['dkim_to_title'] = 'Doeldomein(en) - worden overgeschreven'; $lang['admin']['f2b_parameters'] = 'Fail2ban parameters'; $lang['admin']['f2b_ban_time'] = 'Verbanningstijd (s)'; $lang['admin']['f2b_max_attempts'] = 'Max. pogingen'; @@ -457,6 +485,7 @@ $lang['admin']['dkim_key_unused'] = 'Sleutel ongebruikt'; $lang['admin']['dkim_key_missing'] = 'Sleutel ontbreekt'; $lang['admin']['dkim_add_key'] = 'Voeg ARC/DKIM-sleutel toe'; $lang['admin']['dkim_keys'] = 'ARC/DKIM-sleutels'; +$lang['admin']['dkim_domains_wo_keys'] = "Selecteer domeinen met ontbrekende sleutels"; $lang['admin']['add'] = 'Toevoegen'; $lang['add']['add_domain_restart'] = 'Voeg domein toe en herstart SOGo'; $lang['add']['add_domain_only'] = 'Voeg enkel domein toe'; @@ -485,8 +514,6 @@ $lang['admin']['save'] = 'Sla wijzigingen op'; $lang['admin']['admin'] = 'Beheerder'; $lang['admin']['admin_details'] = 'Wijzig beheerderdetails'; $lang['admin']['unchanged_if_empty'] = 'Laat leeg wanneer onveranderd'; -$lang['admin']['yes'] = '✔'; -$lang['admin']['no'] = '✘'; $lang['admin']['access'] = 'Toegang'; $lang['admin']['no_record'] = 'Geen vermelding'; $lang['admin']['filter_table'] = 'Filtertabel'; @@ -511,7 +538,6 @@ $lang['success']['forwarding_host_added'] = "Doorstuurhost %s is toegevoegd"; $lang['success']['relayhost_removed'] = "Relayhost %s is verwijderd"; $lang['success']['relayhost_added'] = "Relayhost %s is toegevoegd"; $lang['diagnostics']['dns_records'] = 'DNS-vermeldingen'; -$lang['diagnostics']['dns_records_24hours'] = 'Please note that changes made to DNS may take up to 24 hours to correctly have their current state reflected on this page. It is intended as a way for you to easily see how to configure your DNS records and to check whether all your records are correctly stored in DNS.'; $lang['diagnostics']['dns_records_24hours'] = 'Houd er rekening mee dat veranderingen aan DNS tot wel 24 uur in beslag kunnen nemen voordat ze op deze pagina worden weergegeven. Het is bedoeld als een manier om gemakkelijk te zien hoe de DNS-vermeldingen zijn geconfigureerd en om te controleren of alle records correct zijn opgeslagen in DNS.'; $lang['diagnostics']['dns_records_name'] = 'Naam'; $lang['diagnostics']['dns_records_type'] = 'Type'; @@ -571,7 +597,6 @@ $lang['success']['reset_main_logo'] = "Het standaardlogo is hersteld"; $lang['success']['items_released'] = "Geselecteerde onderdelen zijn vrijgegeven"; $lang['success']['item_released'] = "Onderdeel %s vrijgegeven"; $lang['danger']['imagick_exception'] = "Error: Er is een probleem opgetreden met Imagick tijdens het lezen van de afbeelding"; - $lang['quarantine']['quarantine'] = "Quarantaine"; $lang['quarantine']['learn_spam_delete'] = "Onthoud als spam en verwijder"; $lang['quarantine']['qinfo'] = 'Het quarantainesysteem slaat geweigerde e-mail op, terwijl het voor de afzender als niet ontvangen bestempeld is.
"' . $lang['quarantine']['learn_spam_delete'] . '" traint het systeem om toekomstige soortgelijke e-mails direct als spam te classificeren.
Wees er van bewust dat wanneer er meerdere berichten worden onderzocht, dit mogelijk enige tijd kan duren.'; diff --git a/data/web/lang/lang.pl.php b/data/web/lang/lang.pl.php index adc4edab..140b0c32 100644 --- a/data/web/lang/lang.pl.php +++ b/data/web/lang/lang.pl.php @@ -193,8 +193,6 @@ $lang['mailbox']['target_address'] = 'Adres Idź do'; $lang['mailbox']['username'] = 'Nazwa użytkownika'; $lang['mailbox']['fname'] = 'Pełna nazwa'; $lang['mailbox']['filter_table'] = 'Tabela filtru'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; $lang['mailbox']['in_use'] = 'W użyciu (%)'; $lang['mailbox']['msg_num'] = 'Wiadomość #'; $lang['mailbox']['remove'] = 'Usuń'; @@ -360,8 +358,6 @@ $lang['admin']['save'] = 'Zapisz zmiany'; $lang['admin']['admin'] = 'Administrator'; $lang['admin']['admin_details'] = 'Edytuj szczegóły administratora'; $lang['admin']['unchanged_if_empty'] = 'W przypadku braku zmian, nie wypełniaj'; -$lang['admin']['yes'] = '✔'; -$lang['admin']['no'] = '✘'; $lang['admin']['access'] = 'Dostęp'; $lang['admin']['no_record'] = 'Brak rekordu'; $lang['admin']['filter_table'] = 'Tabela filtru'; diff --git a/data/web/lang/lang.pt.php b/data/web/lang/lang.pt.php index 13a86300..d6fc8649 100644 --- a/data/web/lang/lang.pt.php +++ b/data/web/lang/lang.pt.php @@ -140,8 +140,6 @@ $lang['mailbox']['target_address'] = 'Encaminhar para'; $lang['mailbox']['username'] = 'Usuário'; $lang['mailbox']['fname'] = 'Nome'; $lang['mailbox']['filter_table'] = 'Procurar'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; $lang['mailbox']['in_use'] = 'Em uso (%)'; $lang['mailbox']['msg_num'] = 'Mensagens'; $lang['mailbox']['remove'] = 'Remover'; @@ -239,7 +237,5 @@ $lang['admin']['save'] = 'Salvar'; $lang['admin']['admin'] = 'Administrador'; $lang['admin']['admin_details'] = 'Editar informações do administrator'; $lang['admin']['unchanged_if_empty'] = 'Deixar em branco para não alterar'; -$lang['admin']['yes'] = '✔'; -$lang['admin']['no'] = '✘'; $lang['admin']['access'] = 'Acessos'; $lang['admin']['no_record'] = 'Nenhum registro'; diff --git a/data/web/lang/lang.ru.php b/data/web/lang/lang.ru.php index fa2074f6..10a2b30c 100644 --- a/data/web/lang/lang.ru.php +++ b/data/web/lang/lang.ru.php @@ -189,8 +189,6 @@ $lang['mailbox']['target_address'] = 'Основной адрес'; $lang['mailbox']['username'] = 'Имя пользователя'; $lang['mailbox']['fname'] = 'Полное имя'; $lang['mailbox']['filter_table'] = 'Поиск'; -$lang['mailbox']['yes'] = '✔'; -$lang['mailbox']['no'] = '✘'; $lang['mailbox']['in_use'] = 'Использовано (%)'; $lang['mailbox']['msg_num'] = 'Письма #'; $lang['mailbox']['remove'] = 'Удалить'; @@ -359,8 +357,6 @@ $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']['no_record'] = 'Нет записей'; $lang['admin']['filter_table'] = 'Поиск'; diff --git a/data/web/mailbox.php b/data/web/mailbox.php index 837de654..0accf20e 100644 --- a/data/web/mailbox.php +++ b/data/web/mailbox.php @@ -1,8 +1,7 @@
@@ -340,7 +339,8 @@ echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";
+ +