diff --git a/mailServer/mailserverManager.py b/mailServer/mailserverManager.py index 24252a4c2..a44106142 100644 --- a/mailServer/mailserverManager.py +++ b/mailServer/mailserverManager.py @@ -12,6 +12,7 @@ sys.path.append('/usr/local/CyberCP') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") django.setup() from django.http import HttpResponse +from django.db import connection try: from .models import Domains,EUsers from loginSystem.views import loadLoginPage @@ -45,6 +46,57 @@ import threading as multi import argparse + + +def _ensure_email_filter_tables(): + """Create catch-all/plus/pattern email feature tables if missing.""" + create_statements = [ + """CREATE TABLE IF NOT EXISTS `e_catchall` ( + `domain_id` varchar(50) NOT NULL, + `destination` varchar(255) NOT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`domain_id`), + KEY `idx_e_catchall_domain_id` (`domain_id`) +) ENGINE=InnoDB""", + """CREATE TABLE IF NOT EXISTS `e_server_settings` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `plus_addressing_enabled` tinyint(1) NOT NULL DEFAULT 0, + `plus_addressing_delimiter` varchar(1) NOT NULL DEFAULT '+', + PRIMARY KEY (`id`) +) ENGINE=InnoDB""", + """CREATE TABLE IF NOT EXISTS `e_plus_override` ( + `domain_id` varchar(50) NOT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`domain_id`), + KEY `idx_e_plus_override_domain_id` (`domain_id`) +) ENGINE=InnoDB""", + """CREATE TABLE IF NOT EXISTS `e_pattern_forwarding` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `domain_id` varchar(50) NOT NULL, + `pattern` varchar(255) NOT NULL, + `destination` varchar(255) NOT NULL, + `pattern_type` varchar(20) NOT NULL DEFAULT 'wildcard', + `priority` int(11) NOT NULL DEFAULT 100, + `enabled` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + KEY `idx_e_pattern_forwarding_domain_id` (`domain_id`) +) ENGINE=InnoDB""" + ] + + try: + with connection.cursor() as cursor: + for query in create_statements: + cursor.execute(query) + cursor.execute( + """INSERT INTO `e_server_settings` (`id`, `plus_addressing_enabled`, `plus_addressing_delimiter`) + SELECT 1, 0, '+' + WHERE NOT EXISTS (SELECT 1 FROM `e_server_settings` WHERE `id` = 1)""" + ) + return True, '' + except BaseException as msg: + logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [_ensure_email_filter_tables]') + return False, 'Email feature tables are missing and could not be created automatically. Please run CyberPanel upgrade or apply DB migration.' + def _get_email_limits_controller_js(): """Return EmailLimitsNew controller JS: from file or hardcoded fallback so it always works.""" try: @@ -2084,6 +2136,12 @@ protocol sieve { if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0: return ACLManager.loadErrorJson('fetchStatus', 0) + ok, schemaErr = _ensure_email_filter_tables() + if not ok: + data_ret = {'status': 0, 'fetchStatus': 0, 'error_message': schemaErr} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + data = json.loads(self.request.body) domain = data['domain'] @@ -2132,6 +2190,12 @@ protocol sieve { if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0: return ACLManager.loadErrorJson('saveStatus', 0) + ok, schemaErr = _ensure_email_filter_tables() + if not ok: + data_ret = {'status': 0, 'saveStatus': 0, 'error_message': schemaErr} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + data = json.loads(self.request.body) domain = data['domain'] destination = data['destination'] @@ -2190,6 +2254,12 @@ protocol sieve { if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0: return ACLManager.loadErrorJson('deleteStatus', 0) + ok, schemaErr = _ensure_email_filter_tables() + if not ok: + data_ret = {'status': 0, 'deleteStatus': 0, 'error_message': schemaErr} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + data = json.loads(self.request.body) domain = data['domain'] diff --git a/plogical/upgrade.py b/plogical/upgrade.py index c702a55d4..30a9fdcb3 100644 --- a/plogical/upgrade.py +++ b/plogical/upgrade.py @@ -2878,8 +2878,8 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL `destination` varchar(255) NOT NULL, `enabled` tinyint(1) NOT NULL DEFAULT 1, PRIMARY KEY (`domain_id`), - CONSTRAINT `fk_catchall_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4""" + KEY `idx_e_catchall_domain_id` (`domain_id`) +) ENGINE=InnoDB""" try: cursor.execute(query) except: @@ -2890,7 +2890,7 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL `plus_addressing_enabled` tinyint(1) NOT NULL DEFAULT 0, `plus_addressing_delimiter` varchar(1) NOT NULL DEFAULT '+', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4""" +) ENGINE=InnoDB""" try: cursor.execute(query) except: @@ -2900,8 +2900,8 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL `domain_id` varchar(50) NOT NULL, `enabled` tinyint(1) NOT NULL DEFAULT 1, PRIMARY KEY (`domain_id`), - CONSTRAINT `fk_plus_override_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4""" + KEY `idx_e_plus_override_domain_id` (`domain_id`) +) ENGINE=InnoDB""" try: cursor.execute(query) except: @@ -2916,14 +2916,21 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL `priority` int(11) NOT NULL DEFAULT 100, `enabled` tinyint(1) NOT NULL DEFAULT 1, PRIMARY KEY (`id`), - KEY `fk_pattern_domain` (`domain_id`), - CONSTRAINT `fk_pattern_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4""" + KEY `idx_e_pattern_forwarding_domain_id` (`domain_id`) +) ENGINE=InnoDB""" try: cursor.execute(query) except: pass + # Seed singleton row for global email settings if missing. + try: + cursor.execute("""INSERT INTO `e_server_settings` (`id`, `plus_addressing_enabled`, `plus_addressing_delimiter`) +SELECT 1, 0, '+' +WHERE NOT EXISTS (SELECT 1 FROM `e_server_settings` WHERE `id` = 1)""") + except: + pass + try: connection.close() except: