From f2453e316f21ed25219406ba6ecf6e1000c35cae Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 12 Feb 2021 10:04:19 +0100 Subject: [PATCH] [Ejabberd] More fixes for Ejabberd integration (WIP) --- .../ejabberd/mailcowCommandExecutor.php | 8 +- data/Dockerfiles/phpfpm/docker-entrypoint.sh | 3 + data/web/inc/functions.mailbox.inc.php | 4 +- data/web/inc/functions.xmpp.inc.php | 120 ++++++++++++++---- data/web/lang/lang.de.json | 2 + data/web/lang/lang.en.json | 2 + docker-compose.yml | 14 +- 7 files changed, 118 insertions(+), 35 deletions(-) diff --git a/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php b/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php index 2ff33610..390783af 100644 --- a/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php +++ b/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php @@ -98,16 +98,16 @@ class mailcowCommandExecutor implements CommandExecutorInterface { return hash_equals(hash($scheme, $password, true), $hash); case "SMD5": - return verify_salted_hash($hash, $password, 'md5', 16); + return self::verify_salted_hash($hash, $password, 'md5', 16); case "SSHA": - return verify_salted_hash($hash, $password, 'sha1', 20); + return self::verify_salted_hash($hash, $password, 'sha1', 20); case "SSHA256": - return verify_salted_hash($hash, $password, 'sha256', 32); + return self::verify_salted_hash($hash, $password, 'sha256', 32); case "SSHA512": - return verify_salted_hash($hash, $password, 'sha512', 64); + return self::verify_salted_hash($hash, $password, 'sha512', 64); default: return false; diff --git a/data/Dockerfiles/phpfpm/docker-entrypoint.sh b/data/Dockerfiles/phpfpm/docker-entrypoint.sh index 3c9039ae..c880fbf5 100755 --- a/data/Dockerfiles/phpfpm/docker-entrypoint.sh +++ b/data/Dockerfiles/phpfpm/docker-entrypoint.sh @@ -172,6 +172,9 @@ fi # Fix permissions for global filters chown -R 82:82 /global_sieve/* +[[ ! -f /etc/nginx/conf.d/ejabberd.conf ]] && echo '# Autogenerated by mailcow' > /etc/nginx/conf.d/ejabberd.conf +chown 82:82 /etc/nginx/conf.d/ejabberd.conf + # Run hooks for file in /hooks/*; do if [ -x "${file}" ]; then diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 1b7eb183..a2f181c4 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -443,7 +443,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { } $domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46); $description = $_data['description']; - $xmpp_prefix = $_data['xmpp_prefix']; + $xmpp_prefix = preg_replace('/[^\da-z-]/i', '', $_data['xmpp_prefix']); if (empty($description)) { $description = $domain; } @@ -2115,6 +2115,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); continue; } + $xmpp_prefix = preg_replace('/[^\da-z-]/i', '', $xmpp_prefix); $stmt = $pdo->prepare("UPDATE `domain` SET `description` = :description, `gal` = :gal, @@ -2167,6 +2168,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); continue; } + $xmpp_prefix = preg_replace('/[^\da-z-]/i', '', $xmpp_prefix); // todo: should be using api here $stmt = $pdo->prepare("SELECT COUNT(*) AS count, diff --git a/data/web/inc/functions.xmpp.inc.php b/data/web/inc/functions.xmpp.inc.php index c6c5133d..38ac406b 100644 --- a/data/web/inc/functions.xmpp.inc.php +++ b/data/web/inc/functions.xmpp.inc.php @@ -128,55 +128,104 @@ function xmpp_rebuild_configs() { touch('/ejabberd/ejabberd_hosts.yml'); touch('/ejabberd/ejabberd_acl.yml'); + touch('/etc/nginx/conf.d/ejabberd.conf'); $ejabberd_hosts_md5 = md5_file('/ejabberd/ejabberd_hosts.yml'); $ejabberd_acl_md5 = md5_file('/ejabberd/ejabberd_acl.yml'); + $ejabberd_site_md5 = md5_file('/etc/nginx/conf.d/ejabberd.conf'); if (!empty($xmpp_domains)) { // Handle hosts file - $map_handle = fopen('/ejabberd/ejabberd_hosts.yml', 'w'); - if (!$map_handle) { + $hosts_handle = fopen('/ejabberd/ejabberd_hosts.yml', 'w'); + if (!$hosts_handle) { throw new Exception($lang['danger']['file_open_error']); } - fwrite($map_handle, '# Autogenerated by mailcow' . PHP_EOL); - fwrite($map_handle, 'hosts:' . PHP_EOL); + fwrite($hosts_handle, '# Autogenerated by mailcow' . PHP_EOL); + fwrite($hosts_handle, 'hosts:' . PHP_EOL); foreach ($xmpp_domains as $domain => $domain_values) { - fwrite($map_handle, ' - ' . $xmpp_domains[$domain]['xmpp_host'] . PHP_EOL); + fwrite($hosts_handle, ' - ' . $xmpp_domains[$domain]['xmpp_host'] . PHP_EOL); } - fclose($map_handle); + fclose($hosts_handle); // Handle ACL file - $map_handle = fopen('/ejabberd/ejabberd_acl.yml', 'w'); - if (!$map_handle) { + $acl_handle = fopen('/ejabberd/ejabberd_acl.yml', 'w'); + if (!$acl_handle) { throw new Exception($lang['danger']['file_open_error']); } - fwrite($map_handle, '# Autogenerated by mailcow' . PHP_EOL); - fwrite($map_handle, 'append_host_config:' . PHP_EOL); + fwrite($acl_handle, '# Autogenerated by mailcow' . PHP_EOL); + fwrite($acl_handle, 'append_host_config:' . PHP_EOL); foreach ($xmpp_domains as $domain => $domain_values) { - fwrite($map_handle, ' ' . $xmpp_domains[$domain]['xmpp_host'] . ':' . PHP_EOL); - fwrite($map_handle, ' acl:' . PHP_EOL); - fwrite($map_handle, ' admin:' . PHP_EOL); - fwrite($map_handle, ' user:' . PHP_EOL); + fwrite($acl_handle, ' ' . $xmpp_domains[$domain]['xmpp_host'] . ':' . PHP_EOL); + fwrite($acl_handle, ' acl:' . PHP_EOL); + fwrite($acl_handle, ' admin:' . PHP_EOL); + fwrite($acl_handle, ' user:' . PHP_EOL); foreach ($xmpp_domains[$domain]['xmpp_admins'] as $xmpp_admin) { - fwrite($map_handle, ' - ' . $xmpp_admin . PHP_EOL); + fwrite($acl_handle, ' - ' . $xmpp_admin . PHP_EOL); } } - fclose($map_handle); + fclose($acl_handle); + + // Handle Nginx site + $site_handle = @fopen('/etc/nginx/conf.d/ejabberd.conf', 'r+'); + if ($site_handle !== false) { + ftruncate($site_handle, 0); + fclose($site_handle); + } + $site_handle = fopen('/etc/nginx/conf.d/ejabberd.conf', 'w'); + if (!$site_handle) { + throw new Exception($lang['danger']['file_open_error']); + } + fwrite($site_handle, '# Autogenerated by mailcow' . PHP_EOL); + foreach ($xmpp_domains as $domain => $domain_values) { + $site_config = << "reload", "task" => "nginx"), 'Content-type: application/json'), true); + if (isset($response['type']) && $response['type'] == "success") { + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'nginx_reloaded' + ); + } + else { + if (!empty($response['msg'])) { + $error = $response['msg']; + } + else { + $error = '-'; + } + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => array('nginx_reload_failed', htmlspecialchars($error)) + ); + } + } } catch (Exception $e) { $_SESSION['return'][] = array( diff --git a/data/web/lang/lang.de.json b/data/web/lang/lang.de.json index 3a5b9320..2f99e9b0 100644 --- a/data/web/lang/lang.de.json +++ b/data/web/lang/lang.de.json @@ -396,6 +396,7 @@ "max_quota_in_use": "Mailbox-Speicherplatzlimit muss größer oder gleich %d MiB sein", "maxquota_empty": "Max. Speicherplatz pro Mailbox darf nicht 0 sein.", "mysql_error": "MySQL-Fehler: %s", + "nginx_reload_failed": "Nginx Reload ist fehlgeschlagen: %s", "network_host_invalid": "Netzwerk oder Host ungültig: %s", "next_hop_interferes": "%s verhindert das Hinzufügen von Next Hop %s", "next_hop_interferes_any": "Ein vorhandener Eintrag verhindert das Hinzufügen von Next Hop %s", @@ -893,6 +894,7 @@ "mailbox_added": "Mailbox %s wurde angelegt", "mailbox_modified": "Änderungen an Mailbox %s wurden gespeichert", "mailbox_removed": "Mailbox %s wurde entfernt", + "nginx_reloaded": "Nginx wurde neu geladen", "object_modified": "Änderungen an Objekt %s wurden gespeichert", "pushover_settings_edited": "Pushover Konfiguration gespeichert, bitte den Zugang im Anschluss verifizieren.", "qlearn_spam": "Nachricht ID %s wurde als Spam gelernt und gelöscht", diff --git a/data/web/lang/lang.en.json b/data/web/lang/lang.en.json index 41458e1f..92559969 100644 --- a/data/web/lang/lang.en.json +++ b/data/web/lang/lang.en.json @@ -397,6 +397,7 @@ "max_quota_in_use": "Mailbox quota must be greater or equal to %d MiB", "maxquota_empty": "Max. quota per mailbox must not be 0.", "mysql_error": "MySQL error: %s", + "nginx_reload_failed": "Nginx reload failed: %s", "network_host_invalid": "Invalid network or host: %s", "next_hop_interferes": "%s interferes with nexthop %s", "next_hop_interferes_any": "An existing next hop interferes with %s", @@ -894,6 +895,7 @@ "mailbox_added": "Mailbox %s has been added", "mailbox_modified": "Changes to mailbox %s have been saved", "mailbox_removed": "Mailbox %s has been removed", + "nginx_reloaded": "Nginx was reloaded", "object_modified": "Changes to object %s have been saved", "pushover_settings_edited": "Pushover settings successfully set, please verify credentials.", "qlearn_spam": "Message ID %s was learned as spam and deleted", diff --git a/docker-compose.yml b/docker-compose.yml index ec004b31..77ea833c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -101,13 +101,13 @@ services: - rspamd php-fpm-mailcow: - image: mailcow/phpfpm:1.70 + image: mailcow/phpfpm:1.71 command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" depends_on: - redis-mailcow volumes: - ./data/hooks/phpfpm:/hooks:Z - - ./data/web:/web:rw,z + - ./data/web:/web:z - ./data/conf/rspamd/dynmaps:/dynmaps:ro,z - ./data/conf/rspamd/custom/:/rspamd_custom_maps:z - rspamd-vol-1:/var/lib/rspamd:z @@ -123,6 +123,7 @@ services: - ./data/conf/dovecot/global_sieve_after:/global_sieve/after:z - ./data/assets/templates:/tpls:z - ./data/conf/ejabberd/autogen:/ejabberd/:z + - ./data/conf/nginx/:/etc/nginx/conf.d/:z dns: - ${IPV4_NETWORK:-172.22.1}.254 environment: @@ -324,6 +325,7 @@ services: until ping sogo -c1 > /dev/null; do sleep 1; done && until ping redis -c1 > /dev/null; do sleep 1; done && until ping rspamd -c1 > /dev/null; do sleep 1; done && + until ping ejabberd -c1 > /dev/null; do sleep 1; done && exec nginx -g 'daemon off;'" environment: - HTTPS_PORT=${HTTPS_PORT:-443} @@ -337,7 +339,7 @@ services: - ./data/web:/web:ro,z - ./data/conf/rspamd/dynmaps:/dynmaps:ro,z - ./data/assets/ssl/:/etc/ssl/mail/:ro,z - - ./data/conf/nginx/:/etc/nginx/conf.d/:rw,Z + - ./data/conf/nginx/:/etc/nginx/conf.d/:z - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z - sogo-web-vol-1:/usr/lib/GNUstep/SOGo/:z ports: @@ -376,8 +378,8 @@ services: - SNAT_TO_SOURCE=${SNAT_TO_SOURCE:-n} - SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n} volumes: - - ./data/web/.well-known/acme-challenge:/var/www/acme:rw,z - - ./data/assets/ssl:/var/lib/acme/:rw,z + - ./data/web/.well-known/acme-challenge:/var/www/acme:z + - ./data/assets/ssl:/var/lib/acme/:z - ./data/assets/ssl-example:/var/lib/ssl-example/:ro,Z - mysql-socket-vol-1:/var/run/mysqld/:z restart: always @@ -525,7 +527,7 @@ services: - olefy ejabberd-mailcow: - image: mailcow/ejabberd:1.1 + image: mailcow/ejabberd:1.2 volumes: - ./data/conf/ejabberd/ejabberd.yml:/home/ejabberd/conf/ejabberd.yml:z - xmpp-vol-1:/home/ejabberd/database:z