import subprocess import sys from plogical import CyberCPLogFileWriter as logging import shutil import pexpect import os import shlex from plogical.processUtilities import ProcessUtilities from datetime import datetime class installUtilities: Server_root_path = "/usr/local/lsws" @staticmethod def enableEPELRepo(): try: cmd = [] cmd.append("yum") cmd.append("-y") cmd.append("install") cmd.append("epel-release") res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not add EPEL repo " ) print("###############################################") else: print("###############################################") print(" EPEL Repo Added ") print("###############################################") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [enableEPELRepo]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [enableEPELRepo]") return 0 return 1 @staticmethod def addLiteSpeedRepo(): try: # Use the official LiteSpeed repository installation script # This supports all OS versions including CentOS/AlmaLinux/Rocky 7, 8, and 9 cmd = "wget -O - https://repo.litespeed.sh | bash" res = subprocess.call(cmd, shell=True) if res == 1: print("###############################################") print(" Could not add Litespeed repo " ) print("###############################################") else: print("###############################################") print(" Litespeed Repo Added ") print("###############################################") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [addLiteSpeedRepo]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [addLiteSpeedRepo]") return 0 return 1 @staticmethod def installLiteSpeed(): try: cmd = [] cmd.append("yum") cmd.append("-y") cmd.append("install") cmd.append("openlitespeed") res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not install Litespeed " ) print("###############################################") sys.exit() else: print("###############################################") print(" Litespeed Installed ") print("###############################################") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installLiteSpeed]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installLiteSpeed]") return 0 return 1 @staticmethod def startLiteSpeed(): try: cmd = [] cmd.append("/usr/local/lsws/bin/lswsctrl") cmd.append("start") res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not start Litespeed server ") print("###############################################") sys.exit() else: print("###############################################") print(" Litespeed Started ") print("###############################################") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startLiteSpeed]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startLiteSpeed]") return 0 return 1 @staticmethod def reStartLiteSpeed(): try: if ProcessUtilities.decideServer() == ProcessUtilities.OLS: command = "systemctl restart lsws" else: command = "/usr/local/lsws/bin/lswsctrl restart" ProcessUtilities.normalExecutioner(command) except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]") return 0 except BaseException as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]") return 0 return 1 @staticmethod def reStartLiteSpeedSocket(): try: if ProcessUtilities.decideServer() == ProcessUtilities.OLS: command = "sudo systemctl restart lsws" else: command = "sudo /usr/local/lsws/bin/lswsctrl restart" return ProcessUtilities.executioner(command) except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]") return 0 @staticmethod def stopLiteSpeedSocket(): try: if ProcessUtilities.decideServer() == ProcessUtilities.OLS: command = "sudo systemctl stop lsws" else: command = "sudo /usr/local/lsws/bin/lswsctrl stop" return ProcessUtilities.executioner(command) except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartLiteSpeed]") return 0 @staticmethod def reStartOpenLiteSpeed(restart,orestart): try: if ProcessUtilities.decideServer() == ProcessUtilities.OLS: command = "sudo systemctl restart lsws" else: command = "sudo /usr/local/lsws/bin/lswsctrl restart" cmd = shlex.split(command) res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not restart Litespeed serve ") print("###############################################") sys.exit() else: print("###############################################") print(" Litespeed Re-Started ") print("###############################################") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartOpenLiteSpeed]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [reStartOpenLiteSpeed]") return 0 return 1 @staticmethod def safeModifyHttpdConfig(config_modifier, description="config modification"): """ Safely modify httpd_config.conf with backup, validation, and rollback on failure. Prevents corrupted configs that cause OpenLiteSpeed to fail binding ports 80/443. Args: config_modifier: A function that takes file content (list of lines) and returns modified content description: Description of the modification for logging Returns: tuple: (success: bool, error_message: str or None) Reference: https://github.com/usmannasir/cyberpanel/issues/1609 """ config_file = "/usr/local/lsws/conf/httpd_config.conf" if not os.path.exists(config_file): error_msg = f"Config file not found: {config_file}" logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}") return False, error_msg # Create backup with timestamp try: timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") backup_file = f"{config_file}.backup-{timestamp}" shutil.copy2(config_file, backup_file) logging.writeToFile(f"[safeModifyHttpdConfig] Created backup: {backup_file} for {description}") except Exception as e: error_msg = f"Failed to create backup: {str(e)}" logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}") return False, error_msg # Read current config try: with open(config_file, 'r') as f: original_content = f.readlines() except Exception as e: error_msg = f"Failed to read config file: {str(e)}" logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}") return False, error_msg # Modify config using callback try: modified_content = config_modifier(original_content) if not isinstance(modified_content, list): error_msg = "Config modifier must return a list of lines" logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}") return False, error_msg except Exception as e: error_msg = f"Config modifier function failed: {str(e)}" logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}") return False, error_msg # Write modified config try: with open(config_file, 'w') as f: f.writelines(modified_content) except Exception as e: error_msg = f"Failed to write modified config: {str(e)}" logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}") # Restore backup try: shutil.copy2(backup_file, config_file) logging.writeToFile(f"[safeModifyHttpdConfig] Restored backup due to write failure") except: pass return False, error_msg # Validate config using openlitespeed -t try: if ProcessUtilities.decideServer() == ProcessUtilities.OLS: validate_cmd = ['/usr/local/lsws/bin/openlitespeed', '-t', '-f', config_file] else: # For LiteSpeed Enterprise, use lswsctrl validate_cmd = ['/usr/local/lsws/bin/lswsctrl', '-t', '-f', config_file] result = subprocess.run(validate_cmd, capture_output=True, text=True, timeout=30) if result.returncode != 0: error_msg = f"Config validation failed: {result.stderr}" logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}") # Restore backup try: shutil.copy2(backup_file, config_file) logging.writeToFile(f"[safeModifyHttpdConfig] Restored backup due to validation failure") except Exception as restore_error: logging.writeToFile(f"[safeModifyHttpdConfig] CRITICAL: Failed to restore backup: {str(restore_error)}") return False, error_msg except subprocess.TimeoutExpired: error_msg = "Config validation timed out" logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}") # Restore backup try: shutil.copy2(backup_file, config_file) logging.writeToFile(f"[safeModifyHttpdConfig] Restored backup due to validation timeout") except: pass return False, error_msg except Exception as e: error_msg = f"Config validation error: {str(e)}" logging.writeToFile(f"[safeModifyHttpdConfig] {error_msg}") # Restore backup try: shutil.copy2(backup_file, config_file) logging.writeToFile(f"[safeModifyHttpdConfig] Restored backup due to validation error") except: pass return False, error_msg logging.writeToFile(f"[safeModifyHttpdConfig] Successfully modified and validated config: {description}") return True, None @staticmethod def changePortTo80(): try: def modify_config(lines): modified = [] for line in lines: if "*:8088" in line: modified.append(line.replace("*:8088", "*:80")) else: modified.append(line) return modified success, error = installUtilities.safeModifyHttpdConfig( modify_config, "Change port from 8088 to 80" ) if not success: error_msg = error if error else "Unknown error" logging.writeToFile(f"[changePortTo80] Failed: {error_msg}") return 0 return installUtilities.reStartLiteSpeed() except Exception as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [changePortTo80]") return 0 @staticmethod def installAllPHPVersion(): try: cmd = [] cmd.append("yum") cmd.append("groupinstall") cmd.append("lsphp-all") res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not install PHP Binaries ") print("###############################################") sys.exit() else: print("###############################################") print(" PHP Binaries installed ") print("###############################################") writeDataToFile = open(installUtilities.Server_root_path + "/conf/httpd_config.conf", "a") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installAllPHPVersion]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installAllPHPVersion]") return 0 return 1 @staticmethod def installAllPHPToLitespeed(): try: path = installUtilities.Server_root_path + "/conf/" if not os.path.exists(path): shutil.copytree("phpconfigs",path+"phpconfigs") php53 = "include phpconfigs/php53.conf\n" php54 = "include phpconfigs/php54.conf\n" php55 = "include phpconfigs/php55.conf\n" php56 = "include phpconfigs/php56.conf\n" php70 = "include phpconfigs/php70.conf\n" writeDataToFile = open(path+"httpd_config.conf", 'a') writeDataToFile.writelines(php53) writeDataToFile.writelines(php54) writeDataToFile.writelines(php55) writeDataToFile.writelines(php56) writeDataToFile.writelines(php70) writeDataToFile.close() except IOError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installAllPHPToLitespeed]") return 0 return 1 @staticmethod def installMainWebServer(): if installUtilities.enableEPELRepo() == 1: if installUtilities.addLiteSpeedRepo() == 1: if installUtilities.installLiteSpeed() == 1: if installUtilities.startLiteSpeed() == 1: if installUtilities.installAllPHPVersion(): if installUtilities.installAllPHPToLitespeed(): return 1 else: return 0 else: return 0 else: return 0 else: return 0 else: return 0 else: return 0 @staticmethod def removeWebServer(): try: cmd = [] cmd.append("yum") cmd.append("-y") cmd.append("remove") cmd.append("openlitespeed") res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not remove Litespeed ") print("###############################################") sys.exit() else: print("###############################################") print(" Litespeed Removed ") print("###############################################") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]") return 0 try: cmd = [] cmd.append("yum") cmd.append("-y") cmd.append("remove") cmd.append("lsphp*") res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not PHP Binaries ") print("###############################################") else: print("###############################################") print(" PHP Binaries Removed ") print("###############################################") sys.exit() except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]") return 0 try: shutil.rmtree(installUtilities.Server_root_path) except BaseException as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [removeWebServer]") return 0 return 1 @staticmethod def startMariaDB(): ############## Start mariadb ###################### try: cmd = [] cmd.append("systemctl") cmd.append("start") cmd.append("mariadb") res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not start MariaDB ") print("###############################################") sys.exit() else: print("###############################################") print(" MariaDB Started ") print("###############################################") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startMariaDB]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [startMariaDB]") return 0 return 1 @staticmethod def installMySQL(password): try: ############## Install mariadb ###################### cmd = [] cmd.append("yum") cmd.append("-y") cmd.append("install") cmd.append("mariadb-server") res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not install MariaDB ") print("###############################################") sys.exit() else: print("###############################################") print(" MariaDB Installed ") print("###############################################") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installMySQL]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [installMySQL]") return 0 ############## Start mariadb ###################### installUtilities.startMariaDB() ############## Enable mariadb at system startup ###################### try: cmd = [] cmd.append("systemctl") cmd.append("enable") cmd.append("mariadb") res = subprocess.call(cmd) if res == 1: print("###############################################") print(" Could not add mariadb to startup ") print("###############################################") sys.exit() else: print("###############################################") print(" MariaDB Addded to startup ") print("###############################################") except OSError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " Could not add mariadb to startup [installMySQL]") return 0 except ValueError as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " Could not add mariadb to startup [installMySQL]") return 0 if installUtilities.secureMysqlInstallation(password) == 1: return 1 return 0 @staticmethod def secureMysqlInstallation(password): try: expectation = "(enter for none):" securemysql = pexpect.spawn("mysql_secure_installation",maxread=20000) securemysql.expect(expectation) securemysql.sendcontrol('j') expectation = "password? [Y/n]" securemysql.expect(expectation) securemysql.sendline("Y") expectation = "New password:" securemysql.expect(expectation) securemysql.sendline("1qaz@9xvps") expectation = "new password:" securemysql.expect(expectation) securemysql.sendline(password) expectation = "anonymous users? [Y/n]" securemysql.expect(expectation) securemysql.sendline("Y") expectation = "root login remotely? [Y/n]" securemysql.expect(expectation) securemysql.sendline("Y") expectation = "test database and access to it? [Y/n]" securemysql.expect(expectation) securemysql.sendline("Y") expectation = "Reload privilege tables now? [Y/n]" securemysql.expect(expectation) securemysql.sendline("Y") securemysql.wait() if (securemysql.before.find("Thanks for using MariaDB!") > -1 or securemysql.after.find("Thanks for using MariaDB!")>-1): return 1 except pexpect.EOF as msg: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " Exception EOF [installMySQL]") print("###########################Before########################################") print(securemysql.before) print("###########################After########################################") print(securemysql.after) print("########################################################################") except BaseException as msg: print("#############################Before#####################################") print(securemysql.before) print("############################After######################################") print(securemysql.after) print("########################################################################") logging.CyberCPLogFileWriter.writeToFile(str(msg) + "[installMySQL]") return 0 #installUtilities.installAllPHPToLitespeed()