From 8fcaf407e6fa627173c8225e4dda5f7ee533ec9b Mon Sep 17 00:00:00 2001 From: Michael Kuron Date: Mon, 10 Jul 2017 21:02:30 +0200 Subject: [PATCH 1/7] Client configuration link --- data/web/lang/lang.de.php | 1 + data/web/lang/lang.en.php | 1 + data/web/user.php | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index eef13b3f..8706e4ba 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -108,6 +108,7 @@ $lang['user']['user_settings'] = 'Benutzereinstellungen'; $lang['user']['mailbox_settings'] = 'Mailbox-Einstellungen'; $lang['user']['mailbox_details'] = 'Mailbox-Details'; $lang['user']['change_password'] = 'Passwort ändern'; +$lang['user']['client_configuration'] = 'Konfigurationsanleitungen für E-Mail-Programme und Smartphones anzeigen'; $lang['user']['new_password'] = 'Neues Passwort'; $lang['user']['save_changes'] = 'Änderungen speichern'; $lang['user']['password_now'] = 'Aktuelles Passwort (Änderungen bestätigen)'; diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index 3be72fec..fceef80f 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -110,6 +110,7 @@ $lang['user']['user_settings'] = 'User settings'; $lang['user']['mailbox_settings'] = 'Mailbox settings'; $lang['user']['mailbox_details'] = 'Mailbox details'; $lang['user']['change_password'] = 'Change password'; +$lang['user']['client_configuration'] = 'Show configuration guides for email clients and smartphones'; $lang['user']['new_password'] = 'New password'; $lang['user']['save_changes'] = 'Save changes'; $lang['user']['password_now'] = 'Current password (confirm changes)'; diff --git a/data/web/user.php b/data/web/user.php index 77ccc62d..17a06302 100644 --- a/data/web/user.php +++ b/data/web/user.php @@ -64,6 +64,25 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == ' $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; $username = $_SESSION['mailcow_cc_username']; $mailboxdata = mailbox('get', 'mailbox_details', $username); + + $clientconfigstr = "host=" . urlencode($mailcow_hostname) . "&email=" . urlencode($username) . "&name=" . urlencode($mailboxdata['name']) . "&port=" . urlencode($autodiscover_config['caldav']['port']); + if ($autodiscover_config['useEASforOutlook'] == 'yes') + $clientconfigstr .= "&outlookEAS=1"; + if (file_exists('thunderbird-plugins/version.csv')) + { + $fh = fopen('thunderbird-plugins/version.csv', 'r'); + if ($fh) + { + while (($row = fgetcsv($fh, 1000, ';')) !== FALSE) + { + if ($row[0] == 'sogo-integrator@inverse.ca') { + $clientconfigstr .= "&integrator=" . urlencode($row[1]); + } + } + fclose($fh); + } + } + ?>

@@ -74,6 +93,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '

[]

+

[]


From 8741b5f3b1e6613a39bb008aa70adab65c0a05e1 Mon Sep 17 00:00:00 2001 From: andryyy Date: Sat, 16 Sep 2017 13:17:37 +0200 Subject: [PATCH 2/7] [Compose] Enable some minimal inits, change or remove some health checks --- data/web/edit.php | 5 +- data/web/inc/functions.mailbox.inc.php | 114 ++++++++++++++----------- data/web/js/api.js | 4 +- data/web/js/edit.js | 14 +++ data/web/js/mailbox.js | 12 +++ data/web/lang/lang.de.php | 2 + data/web/lang/lang.en.php | 2 + data/web/modals/mailbox.php | 5 +- docker-compose.yml | 46 +++------- generate_config.sh | 7 +- 10 files changed, 120 insertions(+), 91 deletions(-) diff --git a/data/web/edit.php b/data/web/edit.php index 4c60fadf..fbbda18d 100644 --- a/data/web/edit.php +++ b/data/web/edit.php @@ -31,7 +31,10 @@ if (isset($_SESSION['mailcow_cc_role'])) {
- + +
+ +
diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 8c5804d8..1e674e44 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -324,6 +324,7 @@ function mailbox($_action, $_type, $_data = null) { $addresses = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['address'])); $gotos = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['goto'])); $active = intval($_data['active']); + $goto_null = intval($_data['goto_null']); if (empty($addresses[0])) { $_SESSION['return'] = array( 'type' => 'danger', @@ -331,42 +332,47 @@ function mailbox($_action, $_type, $_data = null) { ); return false; } - if (empty($gotos[0])) { + if (empty($gotos[0]) && $goto_null == 0) { $_SESSION['return'] = array( 'type' => 'danger', 'msg' => sprintf($lang['danger']['goto_empty']) ); return false; } - foreach ($gotos as &$goto) { - if (empty($goto)) { - continue; - } - $goto_domain = idn_to_ascii(substr(strstr($goto, '@'), 1)); - $goto_local_part = strstr($goto, '@', true); - $goto = $goto_local_part.'@'.$goto_domain; - $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` - WHERE `kind` REGEXP 'location|thing|group' - AND `username`= :goto"); - $stmt->execute(array(':goto' => $goto)); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if ($num_results != 0) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['goto_invalid']) - ); - return false; - } - if (!filter_var($goto, FILTER_VALIDATE_EMAIL) === true) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['goto_invalid']) - ); - return false; - } + if ($goto_null == "1") { + $goto = "null@localhost"; + } + else { + foreach ($gotos as &$goto) { + if (empty($goto)) { + continue; + } + $goto_domain = idn_to_ascii(substr(strstr($goto, '@'), 1)); + $goto_local_part = strstr($goto, '@', true); + $goto = $goto_local_part.'@'.$goto_domain; + $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` + WHERE `kind` REGEXP 'location|thing|group' + AND `username`= :goto"); + $stmt->execute(array(':goto' => $goto)); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if ($num_results != 0) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['goto_invalid']) + ); + return false; + } + if (!filter_var($goto, FILTER_VALIDATE_EMAIL) === true) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['goto_invalid']) + ); + return false; + } + } + $gotos = array_filter($gotos); + $goto = implode(",", $gotos); } - $gotos = array_filter($gotos); - $goto = implode(",", $gotos); foreach ($addresses as $address) { if (empty($address)) { continue; @@ -1385,6 +1391,7 @@ function mailbox($_action, $_type, $_data = null) { $is_now = mailbox('get', 'alias_details', $address); if (!empty($is_now)) { $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int']; + $goto_null = (isset($_data['goto_null'])) ? intval($_data['goto_null']) : $is_now['goto_null']; $goto = (!empty($_data['goto'])) ? $_data['goto'] : $is_now['goto']; } else { @@ -1394,30 +1401,33 @@ function mailbox($_action, $_type, $_data = null) { ); return false; } - - $gotos = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['goto'])); - foreach ($gotos as &$goto) { - if (empty($goto)) { - continue; - } - if (!filter_var($goto, FILTER_VALIDATE_EMAIL)) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' =>sprintf($lang['danger']['goto_invalid']) - ); - return false; - } - if ($goto == $address) { - $_SESSION['return'] = array( - 'type' => 'danger', - 'msg' => sprintf($lang['danger']['alias_goto_identical']) - ); - return false; - } + if ($goto_null == "1") { + $goto = "null@localhost"; + } + else { + $gotos = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['goto'])); + foreach ($gotos as &$goto) { + if (empty($goto)) { + continue; + } + if (!filter_var($goto, FILTER_VALIDATE_EMAIL)) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' =>sprintf($lang['danger']['goto_invalid']) + ); + return false; + } + if ($goto == $address) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['alias_goto_identical']) + ); + return false; + } + } + $gotos = array_filter($gotos); + $goto = implode(",", $gotos); } - $gotos = array_filter($gotos); - $goto = implode(",", $gotos); - $domain = idn_to_ascii(substr(strstr($address, '@'), 1)); $local_part = strstr($address, '@', true); if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) { diff --git a/data/web/js/api.js b/data/web/js/api.js index 78d111c0..a64ce04f 100644 --- a/data/web/js/api.js +++ b/data/web/js/api.js @@ -67,7 +67,7 @@ $(document).ready(function() { var req_empty = false; $(this).closest("form").find('select, textarea, input').each(function() { if ($(this).prop('required')) { - if (!$(this).val()) { + if (!$(this).val() && $(this).prop('disabled') === false) { req_empty = true; $(this).addClass('inputMissingAttr'); } else { @@ -126,7 +126,7 @@ $(document).ready(function() { var req_empty = false; $(this).closest("form").find('select, textarea, input').each(function() { if ($(this).prop('required')) { - if (!$(this).val()) { + if (!$(this).val() && $(this).prop('disabled') === false) { req_empty = true; $(this).addClass('inputMissingAttr'); } else { diff --git a/data/web/js/edit.js b/data/web/js/edit.js index 4b3654b2..057a9ecb 100644 --- a/data/web/js/edit.js +++ b/data/web/js/edit.js @@ -1,3 +1,17 @@ +$(document).ready(function() { + if ($("#goto_null").is(":checked")) { + $('#textarea_alias_goto').prop('disabled', true); + } + $("#goto_null").click(function( event ) { + if ($("#goto_null").is(":checked")) { + $('#textarea_alias_goto').prop('disabled', true); + } + else { + $("#textarea_alias_goto").removeAttr('disabled'); + } + }); +}); + jQuery(function($){ // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript function validateEmail(email) { diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js index 1347d8a6..c914f644 100644 --- a/data/web/js/mailbox.js +++ b/data/web/js/mailbox.js @@ -30,6 +30,15 @@ $(document).ready(function() { $('#password2').val(random_passwd); }); + $("#goto_null").click(function( event ) { + if ($("#goto_null").is(":checked")) { + $('#textarea_alias_goto').prop('disabled', true); + } + else { + $("#textarea_alias_goto").removeAttr('disabled'); + } + }); + // Log modal $('#logModal').on('show.bs.modal', function(e) { var logText = $(e.relatedTarget).data('log-text'); @@ -283,6 +292,9 @@ jQuery(function($){ if (item.is_catch_all == 1) { item.address = '
Catch-All
' + item.address; } + if (item.goto == "null@localhost") { + item.goto = '⤷ '; + } if (item.in_primary_domain !== "") { item.domain = "↳ " + item.domain + " (" + item.in_primary_domain + ")"; } diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index f857f3e5..a8851a82 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -331,6 +331,7 @@ $lang['edit']['dont_check_sender_acl'] = 'Absender für Domain %s u. Alias-Dom. $lang['edit']['multiple_bookings'] = 'Mehrfaches Buchen'; $lang['edit']['kind'] = 'Art'; $lang['edit']['resource'] = 'Ressource'; +$lang['edit']['goto_null'] = 'Nachrichten sofort verwerfen'; $lang['add']['syncjob'] = 'Sync-Job erstellen'; $lang['add']['syncjob_hint'] = 'Passwörter werden unverschlüsselt abgelegt!'; @@ -385,6 +386,7 @@ $lang['add']['password'] = 'Passwort'; $lang['add']['password_repeat'] = 'Passwort (Wiederholung)'; $lang['add']['previous'] = 'Vorherige Seite'; $lang['add']['restart_sogo_hint'] = 'Der SOGo Container muss nach dem Hinzufügen einer neuen Domain neugestartet werden!'; +$lang['add']['goto_null'] = 'Nachrichten sofort verwerfen'; $lang['login']['title'] = 'Anmeldung'; $lang['login']['administration'] = 'Administration'; diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index 4487e48a..ddb82f23 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -336,6 +336,7 @@ $lang['edit']['dont_check_sender_acl'] = "Disable sender check for domain %s + a $lang['edit']['multiple_bookings'] = 'Multiple bookings'; $lang['edit']['kind'] = 'Kind'; $lang['edit']['resource'] = 'Resource'; +$lang['edit']['goto_null'] = 'Silently discard mail'; $lang['add']['syncjob'] = 'Add sync job'; $lang['add']['syncjob_hint'] = 'Be aware that passwords need to be saved plain-text!'; @@ -390,6 +391,7 @@ $lang['add']['password'] = 'Password'; $lang['add']['password_repeat'] = 'Confirmation password (repeat)'; $lang['add']['previous'] = 'Previous page'; $lang['add']['restart_sogo_hint'] = 'You will need to restart the SOGo service container after adding a new domain!'; +$lang['add']['goto_null'] = 'Silently discard mail'; $lang['login']['title'] = 'Login'; $lang['login']['administration'] = 'Administration'; diff --git a/data/web/modals/mailbox.php b/data/web/modals/mailbox.php index c74e1dff..b726ca6e 100644 --- a/data/web/modals/mailbox.php +++ b/data/web/modals/mailbox.php @@ -234,7 +234,10 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
- + +
+ +

diff --git a/docker-compose.yml b/docker-compose.yml index 2bee1e40..a367d64c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,18 +1,14 @@ -version: '2.1' +version: '2.3' services: unbound-mailcow: image: mailcow/unbound:1.0 build: ./data/Dockerfiles/unbound command: /usr/sbin/unbound + init: true depends_on: mysql-mailcow: condition: service_healthy - healthcheck: - test: ["CMD", "nslookup", "mailcow.email", "127.0.0.1"] - interval: 30s - timeout: 3s - retries: 10 volumes: - ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro restart: always @@ -38,6 +34,7 @@ services: - MYSQL_DATABASE=${DBNAME} - MYSQL_USER=${DBUSER} - MYSQL_PASSWORD=${DBPASS} + init: true restart: always dns: - 172.22.1.254 @@ -50,9 +47,6 @@ services: redis-mailcow: image: redis:alpine - depends_on: - unbound-mailcow: - condition: service_healthy volumes: - redis-vol-1:/data/ restart: always @@ -66,11 +60,12 @@ services: - redis clamd-mailcow: - image: mailcow/clamd:1.2 + image: mailcow/clamd:1.3 build: ./data/Dockerfiles/clamd restart: on-failure environment: - SKIP_CLAMD=${SKIP_CLAMD:-n} + init: true dns: - 172.22.1.254 dns_search: mailcow-network @@ -82,11 +77,7 @@ services: rspamd-mailcow: image: mailcow/rspamd:1.6 build: ./data/Dockerfiles/rspamd - command: > - /bin/bash -c " - sleep 5; - /usr/bin/rspamd -f -u _rspamd -g _rspamd - " + command: "/usr/bin/rspamd -f -u _rspamd -g _rspamd" depends_on: - nginx-mailcow volumes: @@ -97,6 +88,7 @@ services: - dkim-vol-1:/data/dkim - rspamd-vol-1:/var/lib/rspamd restart: always + init: true dns: - 172.22.1.254 dns_search: mailcow-network @@ -142,9 +134,6 @@ services: sogo-mailcow: image: mailcow/sogo:1.8 build: ./data/Dockerfiles/sogo - depends_on: - unbound-mailcow: - condition: service_healthy environment: - DBNAME=${DBNAME} - DBUSER=${DBUSER} @@ -166,9 +155,6 @@ services: dovecot-mailcow: image: mailcow/dovecot:1.8 build: ./data/Dockerfiles/dovecot - depends_on: - unbound-mailcow: - condition: service_healthy volumes: - ./data/conf/dovecot:/usr/local/etc/dovecot - ./data/assets/ssl:/etc/ssl/mail/:ro @@ -204,9 +190,6 @@ services: postfix-mailcow: image: mailcow/postfix:1.4 build: ./data/Dockerfiles/postfix - depends_on: - unbound-mailcow: - condition: service_healthy volumes: - ./data/conf/postfix:/opt/postfix/conf - ./data/assets/ssl:/etc/ssl/mail/:ro @@ -232,9 +215,6 @@ services: memcached-mailcow: image: memcached:alpine - depends_on: - unbound-mailcow: - condition: service_healthy restart: always dns: - 172.22.1.254 @@ -249,15 +229,11 @@ services: - sogo-mailcow - php-fpm-mailcow image: nginx:mainline-alpine - healthcheck: - test: ["CMD", "ping", "php-fpm-mailcow", "-c", "5"] - interval: 5s - timeout: 5s - retries: 10 command: /bin/sh -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active && envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active && envsubst < /etc/nginx/conf.d/templates/server_name.template > /etc/nginx/conf.d/server_name.active && - nginx -g 'daemon off;'" + until ping phpfpm -c1 > /dev/null; do sleep 1; done && + exec nginx -g 'daemon off;'" environment: - HTTPS_PORT=${HTTPS_PORT:-443} - HTTP_PORT=${HTTP_PORT:-80} @@ -283,8 +259,9 @@ services: acme-mailcow: depends_on: - nginx-mailcow - image: mailcow/acme:1.16 + image: mailcow/acme:1.17 build: ./data/Dockerfiles/acme + init: true dns: - 172.22.1.254 dns_search: mailcow-network @@ -319,6 +296,7 @@ services: - redis-mailcow restart: always privileged: true + init: true environment: - TZ=${TZ} - SKIP_FAIL2BAN=${SKIP_FAIL2BAN:-no} diff --git a/generate_config.sh b/generate_config.sh index 4ef1515f..2d03891e 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -16,7 +16,12 @@ if [ -z "$MAILCOW_HOSTNAME" ]; then read -p "Hostname (FQDN): " -ei "mx.example.org" MAILCOW_HOSTNAME fi -[[ -a /etc/timezone ]] && TZ=$(cat /etc/timezone) +if [[ -a /etc/timezone ]]; then + TZ=$(cat /etc/timezone) +elif [[ -a /etc/localtime ]]; then + TZ=$(readlink /etc/localtime|sed -n 's|^.*zoneinfo/||p') +fi + if [ -z "$TZ" ]; then read -p "Timezone: " -ei "Europe/Berlin" TZ else From 2b97305f6da7069416ec98d0e8c9a17d3c24d3e3 Mon Sep 17 00:00:00 2001 From: andryyy Date: Sat, 16 Sep 2017 13:17:48 +0200 Subject: [PATCH 3/7] [ACME] Sleep, don't exit --- data/Dockerfiles/acme/docker-entrypoint.sh | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/data/Dockerfiles/acme/docker-entrypoint.sh b/data/Dockerfiles/acme/docker-entrypoint.sh index c5e6bc36..6715f791 100755 --- a/data/Dockerfiles/acme/docker-entrypoint.sh +++ b/data/Dockerfiles/acme/docker-entrypoint.sh @@ -50,7 +50,8 @@ if [[ -f ${ACME_BASE}/cert.pem ]] && [[ -f ${ACME_BASE}/key.pem ]]; then ISSUER=$(openssl x509 -in ${ACME_BASE}/cert.pem -noout -issuer) if [[ ${ISSUER} != *"Let's Encrypt"* && ${ISSUER} != *"mailcow"* ]]; then echo "Found certificate with issuer other than mailcow snake-oil CA and Let's Encrypt, skipping ACME client..." - exit 0 + sleep 3650d + exec $(readlink -f "$0") else declare -a SAN_ARRAY_NOW SAN_NAMES=$(openssl x509 -noout -text -in ${ACME_BASE}/cert.pem | awk '/X509v3 Subject Alternative Name/ {getline;gsub(/ /, "", $0); print}' | tr -d "DNS:") @@ -79,7 +80,8 @@ fi while true; do if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then echo "SKIP_LETS_ENCRYPT=y, skipping Let's Encrypt..." - exit 0 + sleep 3650d + exec $(readlink -f "$0") fi if [[ "${SKIP_IP_CHECK}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then SKIP_IP_CHECK=y @@ -164,8 +166,10 @@ while true; do # Unique elements ALL_VALIDATED=(${VALIDATED_MAILCOW_HOSTNAME} $(echo ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs)) if [[ -z ${ALL_VALIDATED[*]} ]]; then - echo "Cannot validate hostnames, skipping Let's Encrypt..." - exit 0 + echo "Cannot validate hostnames, skipping Let's Encrypt for 1 hour." + echo "Use SKIP_LETS_ENCRYPT=y in mailcow.conf to skip it permanently." + sleep 1h + exec $(readlink -f "$0") fi ORPHANED_SAN=($(echo ${SAN_ARRAY_NOW[*]} ${ALL_VALIDATED[*]} | tr ' ' '\n' | sort | uniq -u )) @@ -219,7 +223,10 @@ while true; do TRIGGER_RESTART=1 fi [[ ${TRIGGER_RESTART} == 1 ]] && restart_containers ${CONTAINERS_RESTART[*]} - exit 1;; + echo "Retrying in 30 minutes..." + sleep 30m + exec $(readlink -f "$0") + ;; 2) # no change if ! diff ${ACME_BASE}/acme/fullchain.pem ${ACME_BASE}/cert.pem; then echo "Certificate was not changed, but active certificate does not match the verified certificate, fixing and restarting containers..." @@ -253,10 +260,11 @@ while true; do TRIGGER_RESTART=1 fi [[ ${TRIGGER_RESTART} == 1 ]] && restart_containers ${CONTAINERS_RESTART[*]} - exit 1;; + sleep 3650d + ;; esac echo "ACME certificate validation done. Sleeping for another day." - sleep 86400 + sleep 1d done From c6f81fe67bfee66a0b3dca5119cda8cb0b7b11f9 Mon Sep 17 00:00:00 2001 From: andryyy Date: Sat, 16 Sep 2017 13:33:27 +0200 Subject: [PATCH 4/7] [Web] Fix autodiscover merge on user.php --- data/web/autoconfig.php | 26 +++++++++++++------------- data/web/autodiscover.php | 28 ++++++++++++++-------------- data/web/inc/prerequisites.inc.php | 4 +++- data/web/user.php | 21 +++++++++------------ 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/data/web/autoconfig.php b/data/web/autoconfig.php index c905c325..58d6fec1 100644 --- a/data/web/autoconfig.php +++ b/data/web/autoconfig.php @@ -4,7 +4,7 @@ $default_autodiscover_config = $autodiscover_config; if(file_exists('inc/vars.local.inc.php')) { include_once 'inc/vars.local.inc.php'; } -$configuration = array_merge($default_autodiscover_config, $autodiscover_config); +$autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config); error_reporting(0); @@ -33,15 +33,15 @@ header('Content-Type: application/xml'); mail server - - + + SSL %EMAILADDRESS% password-cleartext - - + + STARTTLS %EMAILADDRESS% password-cleartext @@ -51,8 +51,8 @@ header('Content-Type: application/xml'); $records = dns_get_record('_pop3s._tcp.' . $domain, DNS_SRV); // check if POP3 is announced as "not provided" via SRV record if (count($records) == 0 || $records[0]['target'] != '') { ?> - - + + SSL %EMAILADDRESS% password-cleartext @@ -62,8 +62,8 @@ if (count($records) == 0 || $records[0]['target'] != '') { ?> $records = dns_get_record('_pop3._tcp.' . $domain, DNS_SRV); // check if POP3 is announced as "not provided" via SRV record if (count($records) == 0 || $records[0]['target'] != '') { ?> - - + + STARTTLS %EMAILADDRESS% password-cleartext @@ -71,15 +71,15 @@ if (count($records) == 0 || $records[0]['target'] != '') { ?> - - + + SSL %EMAILADDRESS% password-cleartext - - + + STARTTLS %EMAILADDRESS% password-cleartext diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php index 975fd3e0..551cc6dc 100644 --- a/data/web/autodiscover.php +++ b/data/web/autodiscover.php @@ -5,7 +5,7 @@ $default_autodiscover_config = $autodiscover_config; if(file_exists('inc/vars.local.inc.php')) { include_once 'inc/vars.local.inc.php'; } -$configuration = array_merge($default_autodiscover_config, $autodiscover_config); +$autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config); // Redis $redis = new Redis(); @@ -17,14 +17,14 @@ $data = trim(file_get_contents("php://input")); // Desktop client needs IMAP, unless it's Outlook 2013 or higher on Windows if (strpos($data, 'autodiscover/outlook/responseschema') !== false) { // desktop client - $configuration['autodiscoverType'] = 'imap'; - if ($configuration['useEASforOutlook'] == 'yes' && + $autodiscover_config['autodiscoverType'] = 'imap'; + if ($autodiscover_config['useEASforOutlook'] == 'yes' && // Office for macOS does not support EAS strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') === false && // Outlook 2013 (version 15) or higher preg_match('/(Outlook|Office).+1[5-9]\./', $_SERVER['HTTP_USER_AGENT']) ) { - $configuration['autodiscoverType'] = 'activesync'; + $autodiscover_config['autodiscoverType'] = 'activesync'; } } @@ -88,7 +88,7 @@ else { $displayname = $email; } - if ($configuration['autodiscoverType'] == 'imap') { + if ($autodiscover_config['autodiscoverType'] == 'imap') { ?> @@ -99,8 +99,8 @@ else { settings IMAP - - + + off off @@ -109,8 +109,8 @@ else { SMTP - - + + off off @@ -121,13 +121,13 @@ else { CalDAV - https:///SOGo/dav//Calendar + https:///SOGo/dav//Calendar off CardDAV - https:///SOGo/dav//Contacts + https:///SOGo/dav//Contacts off @@ -135,7 +135,7 @@ else { en:en @@ -147,8 +147,8 @@ else { MobileSync - - + + diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index a394943e..0ba894f7 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -1,8 +1,10 @@

From e4f13568d1131cf9234a09a45586741677f1364c Mon Sep 17 00:00:00 2001 From: Michael Kuron Date: Sat, 16 Sep 2017 18:46:28 +0200 Subject: [PATCH 5/7] Rspamd user settings: fix matching From header --- data/conf/rspamd/dynmaps/settings.php | 8 ++++---- docker-compose.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/conf/rspamd/dynmaps/settings.php b/data/conf/rspamd/dynmaps/settings.php index d6ca8a53..552918e1 100644 --- a/data/conf/rspamd/dynmaps/settings.php +++ b/data/conf/rspamd/dynmaps/settings.php @@ -191,8 +191,8 @@ while ($row = array_shift($rows)) { $grouped_lists = $stmt->fetchAll(PDO::FETCH_COLUMN); $value_sane = preg_replace("/\.\./", ".", (preg_replace("/\*/", ".*", $grouped_lists[0]))); ?> - request_header = { - "From" = "()"; + header = { + "From" = "/()/i"; } fetchAll(PDO::FETCH_COLUMN); $value_sane = preg_replace("/\.\./", ".", (preg_replace("/\*/", ".*", $grouped_lists[0]))); ?> - request_header = { - "From" = "()"; + header = { + "From" = "/()/i"; } Date: Sat, 16 Sep 2017 22:59:42 +0200 Subject: [PATCH 6/7] [Web] New feature: Show DKIM private keys - needs variable set to true in vars.local.inc.php --- data/web/admin.php | 5 +- data/web/inc/functions.dkim.inc.php | 8 +++ data/web/inc/vars.inc.php | 3 + data/web/js/admin.js | 102 ++++++++++++++++++++++++++++ data/web/modals/admin.php | 14 ++++ 5 files changed, 131 insertions(+), 1 deletion(-) diff --git a/data/web/admin.php b/data/web/admin.php index 062b917d..714a31d2 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -157,6 +157,7 @@ $tfa_data = get_tfa();
+

↪ Private key

+

↪ Private key

↳ Alias-Domain:

-
-
+
-
+

↪ Private key

hGet('DKIM_SELECTORS', $_data); + $dkimdata['privkey'] = $redis->hGet('DKIM_PRIV_KEYS', $dkimdata['dkim_selector'] . $_data); + if ($GLOBALS['SHOW_DKIM_PRIV_KEYS'] === true) { + $dkimdata['privkey'] = base64_encode($redis->hGet('DKIM_PRIV_KEYS', $dkimdata['dkim_selector'] . '.' . $_data)); + } + else { + $dkimdata['privkey'] = base64_encode('Please set $SHOW_DKIM_PRIV_KEYS to true to show DKIM private keys.'); + } + } return $dkimdata; break; diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index cf7458cf..dfb40bea 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -82,6 +82,9 @@ $DEFAULT_THEME = 'lumen'; // Password complexity as regular expression $PASSWD_REGEP = '.{4,}'; +// Show DKIM private keys - false by default +$SHOW_DKIM_PRIV_KEYS = false; + // mailcow Apps - buttons on login screen $MAILCOW_APPS = array( array( diff --git a/data/web/js/admin.js b/data/web/js/admin.js index 26bf96cd..7628da82 100644 --- a/data/web/js/admin.js +++ b/data/web/js/admin.js @@ -1,3 +1,96 @@ +var Base64 = { + _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + encode: function(e) { + var t = ""; + var n, r, i, s, o, u, a; + var f = 0; + e = Base64._utf8_encode(e); + while (f < e.length) { + n = e.charCodeAt(f++); + r = e.charCodeAt(f++); + i = e.charCodeAt(f++); + s = n >> 2; + o = (n & 3) << 4 | r >> 4; + u = (r & 15) << 2 | i >> 6; + a = i & 63; + if (isNaN(r)) { + u = a = 64 + } else if (isNaN(i)) { + a = 64 + } + t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + + this._keyStr.charAt(u) + this._keyStr.charAt(a) + } + return t + }, + decode: function(e) { + var t = ""; + var n, r, i; + var s, o, u, a; + var f = 0; + e = e.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + while (f < e.length) { + s = this._keyStr.indexOf(e.charAt(f++)); + o = this._keyStr.indexOf(e.charAt(f++)); + u = this._keyStr.indexOf(e.charAt(f++)); + a = this._keyStr.indexOf(e.charAt(f++)); + n = s << 2 | o >> 4; + r = (o & 15) << 4 | u >> 2; + i = (u & 3) << 6 | a; + t = t + String.fromCharCode(n); + if (u != 64) { + t = t + String.fromCharCode(r) + } + if (a != 64) { + t = t + String.fromCharCode(i) + } + } + t = Base64._utf8_decode(t); + return t + }, + _utf8_encode: function(e) { + e = e.replace(/\r\n/g, "\n"); + var t = ""; + for (var n = 0; n < e.length; n++) { + var r = e.charCodeAt(n); + if (r < 128) { + t += String.fromCharCode(r) + } else if (r > 127 && r < 2048) { + t += String.fromCharCode(r >> 6 | 192); + t += String.fromCharCode(r & 63 | 128) + } else { + t += String.fromCharCode(r >> 12 | 224); + t += String.fromCharCode(r >> 6 & 63 | 128); + t += String.fromCharCode(r & 63 | 128) + } + } + return t + }, + _utf8_decode: function(e) { + var t = ""; + var n = 0; + var r = c1 = c2 = 0; + while (n < e.length) { + r = e.charCodeAt(n); + if (r < 128) { + t += String.fromCharCode(r); + n++ + } else if (r > 191 && r < 224) { + c2 = e.charCodeAt(n + 1); + t += String.fromCharCode((r & 31) << 6 | c2 & 63); + n += 2 + } else { + c2 = e.charCodeAt(n + 1); + c3 = e.charCodeAt(n + 2); + t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << + 6 | c3 & 63); + n += 3 + } + } + return t + } +} + jQuery(function($){ // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery var entityMap = { @@ -560,6 +653,15 @@ jQuery(function($){ } }) + $('#showDKIMprivKey').on('show.bs.modal', function (e) { + $('#priv_key_pre').text("-"); + p_related = $(e.relatedTarget) + if (p_related != null) { + var decoded_key = Base64.decode((p_related.data('priv-key'))); + $('#priv_key_pre').text(decoded_key); + } + }) + $('#test_relayhost').on('click', function (e) { e.preventDefault(); prev = $('#test_relayhost').text(); diff --git a/data/web/modals/admin.php b/data/web/modals/admin.php index cce43b47..bf17296c 100644 --- a/data/web/modals/admin.php +++ b/data/web/modals/admin.php @@ -91,3 +91,17 @@ if (!isset($_SESSION['mailcow_cc_role'])) { + + From 1b974bc8d18797c711978040e768a66ada75df1a Mon Sep 17 00:00:00 2001 From: andryyy Date: Sat, 16 Sep 2017 23:05:33 +0200 Subject: [PATCH 7/7] [Compose] New images --- data/Dockerfiles/rspamd/Dockerfile | 6 +++--- docker-compose.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/Dockerfiles/rspamd/Dockerfile b/data/Dockerfiles/rspamd/Dockerfile index 32e0fe30..07250232 100644 --- a/data/Dockerfiles/rspamd/Dockerfile +++ b/data/Dockerfiles/rspamd/Dockerfile @@ -18,9 +18,9 @@ RUN apt-get update && apt-get install -y \ && mkdir -p /run/rspamd \ && chown _rspamd:_rspamd /run/rspamd -COPY settings.conf /etc/rspamd/modules.d/settings.conf -COPY ratelimit.lua /usr/share/rspamd/lua/ratelimit.lua -COPY lua_util.lua /usr/share/rspamd/lib/lua_util.lua +#COPY settings.conf /etc/rspamd/modules.d/settings.conf +#COPY ratelimit.lua /usr/share/rspamd/lua/ratelimit.lua +#COPY lua_util.lua /usr/share/rspamd/lib/lua_util.lua COPY docker-entrypoint.sh /docker-entrypoint.sh ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml index a367d64c..fe7a2f5d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -75,7 +75,7 @@ services: - clamd rspamd-mailcow: - image: mailcow/rspamd:1.6 + image: mailcow/rspamd:1.7 build: ./data/Dockerfiles/rspamd command: "/usr/bin/rspamd -f -u _rspamd -g _rspamd" depends_on: