diff --git a/install/install.py b/install/install.py index 52d4c17b6..94f73072e 100644 --- a/install/install.py +++ b/install/install.py @@ -4695,26 +4695,57 @@ milter_default_action = accept def enableDisableEmail(state): try: servicePath = '/home/cyberpanel/postfix' - + if state == 'off': - - command = 'sudo systemctl stop postfix' - subprocess.call(shlex.split(command)) - - command = 'sudo systemctl disable postfix' - subprocess.call(shlex.split(command)) - + # Stop and disable postfix service if it exists try: - os.remove(servicePath) + command = 'systemctl stop postfix' + subprocess.call(shlex.split(command), + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + + command = 'systemctl disable postfix' + subprocess.call(shlex.split(command), + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) except: pass - - else: - writeToFile = open(servicePath, 'w+') - writeToFile.close() - + + # Remove marker file if it exists + if os.path.exists(servicePath): + try: + os.remove(servicePath) + logging.InstallLog.writeToFile( + 'Removed postfix marker file (mail services disabled)' + ) + except Exception as e: + logging.InstallLog.writeToFile( + f'Warning: Could not remove postfix marker: {str(e)}' + ) + + else: # state == 'on' + # Only create marker if postfix is actually installed + if (os.path.exists('/usr/sbin/postfix') or + os.path.exists('/usr/bin/postfix')): + # Ensure /home/cyberpanel exists + if not os.path.exists('/home/cyberpanel'): + os.makedirs('/home/cyberpanel', mode=0o755) + + # Create marker file + with open(servicePath, 'w+') as f: + f.write('') + logging.InstallLog.writeToFile( + 'Created postfix marker file (mail services enabled)' + ) + else: + logging.InstallLog.writeToFile( + 'Skipping postfix marker creation (postfix not installed)' + ) + except OSError as msg: - logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [enableDisableEmail]") + logging.InstallLog.writeToFile( + '[ERROR] ' + str(msg) + " [enableDisableEmail]" + ) return 0 @staticmethod @@ -5574,14 +5605,22 @@ def main(): checks.cyberpanel_db_password = checks.mysql_Root_password checks.setup_email_Passwords(checks.cyberpanel_db_password, mysql) checks.setup_postfix_dovecot_config(mysql) + # Create marker immediately after successful install + checks.enableDisableEmail('on') + elif args.postfix == 'ON': + checks.install_postfix_dovecot() + # Ensure cyberpanel_db_password is set before calling setup_email_Passwords + if not hasattr(checks, 'cyberpanel_db_password') or checks.cyberpanel_db_password is None: + checks.cyberpanel_db_password = checks.mysql_Root_password + checks.setup_email_Passwords(checks.cyberpanel_db_password, mysql) + checks.setup_postfix_dovecot_config(mysql) + # Create marker immediately after successful install + checks.enableDisableEmail('on') else: - if args.postfix == 'ON': - checks.install_postfix_dovecot() - # Ensure cyberpanel_db_password is set before calling setup_email_Passwords - if not hasattr(checks, 'cyberpanel_db_password') or checks.cyberpanel_db_password is None: - checks.cyberpanel_db_password = checks.mysql_Root_password - checks.setup_email_Passwords(checks.cyberpanel_db_password, mysql) - checks.setup_postfix_dovecot_config(mysql) + # User explicitly disabled postfix + preFlightsChecks.stdOut("Skipping Postfix/Mail services installation as requested.") + # Ensure marker doesn't exist + checks.enableDisableEmail('off') checks.install_unzip() checks.install_zip() @@ -5606,10 +5645,12 @@ def main(): if args.postfix is None: checks.installOpenDKIM() checks.configureOpenDKIM() + elif args.postfix == 'ON': + checks.installOpenDKIM() + checks.configureOpenDKIM() else: - if args.postfix == 'ON': - checks.installOpenDKIM() - checks.configureOpenDKIM() + # Skip OpenDKIM when postfix is disabled + preFlightsChecks.stdOut("Skipping OpenDKIM installation (mail services disabled).") checks.modSecPreReqs() checks.installLSCPD() @@ -5621,12 +5662,6 @@ def main(): if args.redis is not None: checks.installRedis() - if args.postfix is not None: - checks.enableDisableEmail(args.postfix.lower()) - else: - preFlightsChecks.stdOut("Postfix will be installed and enabled.") - checks.enableDisableEmail('on') - if args.powerdns is not None: checks.enableDisableDNS(args.powerdns.lower()) else: @@ -5639,6 +5674,26 @@ def main(): preFlightsChecks.stdOut("Pure-FTPD will be installed and enabled.") checks.enableDisableFTP('on', distro) + # Validate service marker files match actual installations + logging.InstallLog.writeToFile("Validating service markers...") + + # Check postfix marker + postfix_marker = '/home/cyberpanel/postfix' + postfix_installed = (os.path.exists('/etc/postfix/main.cf') and + (os.path.exists('/usr/sbin/postfix') or + os.path.exists('/usr/bin/postfix'))) + + if os.path.exists(postfix_marker) and not postfix_installed: + logging.InstallLog.writeToFile( + 'Warning: Postfix marker exists but postfix not installed. Removing marker.' + ) + os.remove(postfix_marker) + elif not os.path.exists(postfix_marker) and postfix_installed: + logging.InstallLog.writeToFile( + 'Warning: Postfix installed but marker missing. Creating marker.' + ) + checks.enableDisableEmail('on') + checks.installCLScripts() # checks.disablePackegeUpdates() diff --git a/plogical/mailUtilities.py b/plogical/mailUtilities.py index 8f7bae67c..4b727c1c7 100644 --- a/plogical/mailUtilities.py +++ b/plogical/mailUtilities.py @@ -51,6 +51,31 @@ class mailUtilities: print("Successfully sent email") except BaseException as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg)) + + @staticmethod + def isPostfixInstalled(): + """ + Check if Postfix is actually installed and available + Returns True if postfix is installed, False otherwise + """ + try: + # Check if postfix binary exists + if not (os.path.exists('/usr/sbin/postfix') or + os.path.exists('/usr/bin/postfix')): + return False + + # Check if postfix main.cf exists + if not os.path.exists('/etc/postfix/main.cf'): + return False + + # Check if postfix service is available + command = 'systemctl list-unit-files postfix.service' + result = ProcessUtilities.outputExecutioner(command) + + return 'postfix.service' in result + except: + return False + @staticmethod def AfterEffects(domain): path = "/usr/local/CyberCP/install/rainloop/cyberpanel.net.ini" diff --git a/plogical/virtualHostUtilities.py b/plogical/virtualHostUtilities.py index b0588ab39..8a78991a8 100644 --- a/plogical/virtualHostUtilities.py +++ b/plogical/virtualHostUtilities.py @@ -658,9 +658,33 @@ local_name %s { postfixPath = '/home/cyberpanel/postfix' if os.path.exists(postfixPath): - retValues = mailUtilities.setupDKIM(virtualHostName) - if retValues[0] == 0: - raise BaseException(retValues[1]) + # Verify postfix is actually installed before attempting DKIM + if (os.path.exists('/etc/postfix/main.cf') and + (os.path.exists('/usr/sbin/postfix') or os.path.exists('/usr/bin/postfix'))): + try: + retValues = mailUtilities.setupDKIM(virtualHostName) + if retValues[0] == 0: + # Log warning but don't fail website creation + logging.CyberCPLogFileWriter.statusWriter( + tempStatusPath, + 'Warning: DKIM setup failed, continuing without mail...' + ) + except Exception as e: + # Log error but don't fail website creation + logging.CyberCPLogFileWriter.statusWriter( + tempStatusPath, + f'Warning: DKIM error: {str(e)}, continuing without mail...' + ) + else: + # Postfix marker exists but postfix not installed - clean up marker + logging.CyberCPLogFileWriter.statusWriter( + tempStatusPath, + 'Removing stale postfix marker file...' + ) + try: + os.remove(postfixPath) + except: + pass retValues = vhost.createDirectoryForVirtualHost(virtualHostName, administratorEmail, virtualHostUser, phpVersion, openBasedir)