import json import os import os.path import sys import argparse import pwd import grp import re sys.path.append('/usr/local/CyberCP') _install_dir = '/usr/local/CyberCP/install' if _install_dir not in sys.path: sys.path.insert(0, _install_dir) import ols_binaries_config os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") from plogical.errorSanitizer import ErrorSanitizer from plogical.installUtilities import installUtilities import shlex import subprocess import shutil import time import MySQLdb as mysql import random import string def update_all_config_files_with_password(new_password): """ Update all configuration files that use the cyberpanel database password. This includes FTP, PowerDNS, Postfix, Dovecot configurations. """ config_updates = [ # Django settings { 'path': '/usr/local/CyberCP/CyberCP/settings.py', 'updates': [ (r"('cyberpanel'[^}]+?'PASSWORD':\s*')[^']+'", r"\1%s'" % new_password) ] }, # FTP configurations { 'path': '/etc/pure-ftpd/pureftpd-mysql.conf', 'updates': [ (r'^MYSQLPassword\s+.*$', 'MYSQLPassword %s' % new_password) ] }, { 'path': '/etc/pure-ftpd/db/mysql.conf', # Ubuntu specific 'updates': [ (r'^MYSQLPassword\s+.*$', 'MYSQLPassword %s' % new_password) ] }, # PowerDNS configurations { 'path': '/etc/pdns/pdns.conf', # CentOS/RHEL 'updates': [ (r'^gmysql-password=.*$', 'gmysql-password=%s' % new_password) ] }, { 'path': '/etc/powerdns/pdns.conf', # Ubuntu/Debian 'updates': [ (r'^gmysql-password=.*$', 'gmysql-password=%s' % new_password) ] }, # Postfix MySQL configurations { 'path': '/etc/postfix/mysql-virtual_domains.cf', 'updates': [ (r'^password\s*=.*$', 'password = %s' % new_password) ] }, { 'path': '/etc/postfix/mysql-virtual_forwardings.cf', 'updates': [ (r'^password\s*=.*$', 'password = %s' % new_password) ] }, { 'path': '/etc/postfix/mysql-virtual_mailboxes.cf', 'updates': [ (r'^password\s*=.*$', 'password = %s' % new_password) ] }, { 'path': '/etc/postfix/mysql-virtual_email2email.cf', 'updates': [ (r'^password\s*=.*$', 'password = %s' % new_password) ] }, # Dovecot MySQL configuration { 'path': '/etc/dovecot/dovecot-sql.conf.ext', 'updates': [ (r'^connect\s*=.*$', lambda m: update_dovecot_connect_string(m.group(0), new_password)) ] } ] for config in config_updates: if not os.path.exists(config['path']): continue try: with open(config['path'], 'r') as f: content = f.read() original_content = content for pattern, replacement in config['updates']: if callable(replacement): # For complex replacements like dovecot connect string content = re.sub(pattern, replacement, content, flags=re.MULTILINE) else: content = re.sub(pattern, replacement, content, flags=re.MULTILINE) if content != original_content: with open(config['path'], 'w') as f: f.write(content) print("[RECOVERY] Updated password in: %s" % config['path']) except Exception as e: print("[RECOVERY] Warning: Could not update %s: %s" % (config['path'], str(e))) def update_dovecot_connect_string(connect_line, new_password): """ Update the password in dovecot's connect string. Format: connect = host=localhost dbname=cyberpanel user=cyberpanel password=oldpass """ # Replace the password part in the connect string updated = re.sub(r'password=\S+', 'password=%s' % new_password, connect_line) return updated def restart_affected_services(): """ Restart services that use the cyberpanel database password. """ services_to_restart = [ 'pure-ftpd', # FTP service 'postfix', # Mail transfer agent 'dovecot', # IMAP/POP3 server 'pdns', # PowerDNS (CentOS/RHEL) 'powerdns', # PowerDNS (Ubuntu/Debian) ] for service in services_to_restart: try: # Try systemctl first (systemd) result = subprocess.run(['systemctl', 'restart', service], capture_output=True, text=True) if result.returncode == 0: print("[RECOVERY] Restarted service: %s" % service) elif 'Unit' in result.stderr and 'not found' in result.stderr: # Service doesn't exist, skip pass else: # Try service command (older systems) result = subprocess.run(['service', service, 'restart'], capture_output=True, text=True) if result.returncode == 0: print("[RECOVERY] Restarted service: %s" % service) except Exception as e: print("[RECOVERY] Warning: Could not restart %s: %s" % (service, str(e))) # Try to import settings, but handle case where CyberCP directory is damaged try: from CyberCP import settings except ImportError: print("WARNING: Cannot import CyberCP settings. Attempting recovery...") def recover_database_credentials(): """Attempt to recover or reset database credentials""" # First, ensure we have root MySQL password if not os.path.exists('/etc/cyberpanel/mysqlPassword'): print("FATAL: Cannot find MySQL root password file at /etc/cyberpanel/mysqlPassword") print("Manual intervention required.") sys.exit(1) root_password = open('/etc/cyberpanel/mysqlPassword', 'r').read().strip() cyberpanel_password = None # Try to read existing settings.py to get cyberpanel password settings_path = '/usr/local/CyberCP/CyberCP/settings.py' if os.path.exists(settings_path): try: with open(settings_path, 'r') as f: settings_content = f.read() import re # Extract cyberpanel database password db_pattern = r"'default':[^}]*'USER':\s*'cyberpanel'[^}]*'PASSWORD':\s*'([^']+)'" match = re.search(db_pattern, settings_content, re.DOTALL) if match: cyberpanel_password = match.group(1) print("Found existing cyberpanel password in settings.py") # Test if this password actually works try: test_conn = mysql.connect(host='localhost', user='cyberpanel', passwd=cyberpanel_password, db='cyberpanel') test_conn.close() print("Verified cyberpanel database credentials are valid") except: print("Found password in settings.py but it doesn't work, will reset") cyberpanel_password = None except Exception as e: print("Could not extract password from settings.py: %s" % str(e)) # If we couldn't get a working password, we need to reset it if cyberpanel_password is None: print("Resetting cyberpanel database user password...") # Check if we're on Ubuntu or CentOS # On Ubuntu, cyberpanel uses root password; on CentOS, it uses a separate password if os.path.exists('/etc/lsb-release'): # Ubuntu - use root password cyberpanel_password = root_password reset_to_root = True else: # CentOS/others - generate new password chars = string.ascii_letters + string.digits cyberpanel_password = ''.join(random.choice(chars) for _ in range(14)) reset_to_root = False try: # Connect as root and reset cyberpanel user conn = mysql.connect(host='localhost', user='root', passwd=root_password) cursor = conn.cursor() # Check if cyberpanel database exists cursor.execute("SHOW DATABASES LIKE 'cyberpanel'") if not cursor.fetchone(): print("Creating cyberpanel database...") cursor.execute("CREATE DATABASE IF NOT EXISTS cyberpanel") # Reset cyberpanel user - drop and recreate to ensure clean state cursor.execute("DROP USER IF EXISTS 'cyberpanel'@'localhost'") cursor.execute("CREATE USER 'cyberpanel'@'localhost' IDENTIFIED BY '%s'" % cyberpanel_password) cursor.execute("GRANT ALL PRIVILEGES ON cyberpanel.* TO 'cyberpanel'@'localhost'") cursor.execute("FLUSH PRIVILEGES") conn.close() if reset_to_root: print("Reset cyberpanel user password to match root password (Ubuntu style)") else: print("Reset cyberpanel user with new generated password (CentOS style)") # Update all configuration files with the new password print("Updating all service configuration files with new password...") update_all_config_files_with_password(cyberpanel_password) # Restart affected services to pick up new configuration print("Restarting affected services...") restart_affected_services() # Save the password to a temporary file for the upgrade process temp_pass_file = '/tmp/cyberpanel_recovered_password' with open(temp_pass_file, 'w') as f: f.write(cyberpanel_password) os.chmod(temp_pass_file, 0o600) print("Saved recovered password to temporary file") except Exception as e: print("Failed to reset cyberpanel database user: %s" % str(e)) print("Manual intervention required. Please run:") print(" mariadb -u root -p") print(" CREATE DATABASE IF NOT EXISTS cyberpanel;") print(" GRANT ALL PRIVILEGES ON cyberpanel.* TO 'cyberpanel'@'localhost' IDENTIFIED BY 'your_password';") print(" FLUSH PRIVILEGES;") sys.exit(1) return cyberpanel_password, root_password # Perform recovery cyberpanel_password, root_password = recover_database_credentials() # Create a minimal settings object for recovery class MinimalSettings: DATABASES = { 'default': { 'NAME': 'cyberpanel', 'USER': 'cyberpanel', 'PASSWORD': cyberpanel_password, 'HOST': 'localhost', 'PORT': '3306' }, 'rootdb': { 'NAME': 'mysql', 'USER': 'root', 'PASSWORD': root_password, 'HOST': 'localhost', 'PORT': '3306' } } settings = MinimalSettings() print("Recovery complete. Continuing with upgrade...") VERSION = '2.5.5' BUILD = 5 CENTOS7 = 0 CENTOS8 = 1 Ubuntu18 = 2 Ubuntu20 = 3 CloudLinux7 = 4 CloudLinux8 = 5 openEuler20 = 6 openEuler22 = 7 Ubuntu22 = 8 Ubuntu24 = 9 Debian11 = 10 Debian12 = 11 Debian13 = 12 class Upgrade: logPath = "/usr/local/lscp/logs/upgradeLog" cdn = 'cdn.cyberpanel.sh' installedOutput = '' CentOSPath = '/etc/redhat-release' UbuntuPath = '/etc/lsb-release' openEulerPath = '/etc/openEuler-release' DebianPath = '/etc/os-release' FromCloud = 0 SnappyVersion = '2.38.2' LogPathNew = '/home/cyberpanel/upgrade_logs' SoftUpgrade = 0 AdminACL = '{"adminStatus":1, "versionManagement": 1, "createNewUser": 1, "listUsers": 1, "deleteUser":1 , "resellerCenter": 1, ' \ '"changeUserACL": 1, "createWebsite": 1, "modifyWebsite": 1, "suspendWebsite": 1, "deleteWebsite": 1, ' \ '"createPackage": 1, "listPackages": 1, "deletePackage": 1, "modifyPackage": 1, "createDatabase": 1, "deleteDatabase": 1, ' \ '"listDatabases": 1, "createNameServer": 1, "createDNSZone": 1, "deleteZone": 1, "addDeleteRecords": 1, ' \ '"createEmail": 1, "listEmails": 1, "deleteEmail": 1, "emailForwarding": 1, "changeEmailPassword": 1, ' \ '"dkimManager": 1, "createFTPAccount": 1, "deleteFTPAccount": 1, "listFTPAccounts": 1, "createBackup": 1,' \ ' "restoreBackup": 1, "addDeleteDestinations": 1, "scheduleBackups": 1, "remoteBackups": 1, "googleDriveBackups": 1, "manageSSL": 1, ' \ '"hostnameSSL": 1, "mailServerSSL": 1 }' ResellerACL = '{"adminStatus":0, "versionManagement": 1, "createNewUser": 1, "listUsers": 1, "deleteUser": 1 , "resellerCenter": 1, ' \ '"changeUserACL": 0, "createWebsite": 1, "modifyWebsite": 1, "suspendWebsite": 1, "deleteWebsite": 1, ' \ '"createPackage": 1, "listPackages": 1, "deletePackage": 1, "modifyPackage": 1, "createDatabase": 1, "deleteDatabase": 1, ' \ '"listDatabases": 1, "createNameServer": 1, "createDNSZone": 1, "deleteZone": 1, "addDeleteRecords": 1, ' \ '"createEmail": 1, "listEmails": 1, "deleteEmail": 1, "emailForwarding": 1, "changeEmailPassword": 1, ' \ '"dkimManager": 1, "createFTPAccount": 1, "deleteFTPAccount": 1, "listFTPAccounts": 1, "createBackup": 1,' \ ' "restoreBackup": 1, "addDeleteDestinations": 0, "scheduleBackups": 0, "remoteBackups": 0, "googleDriveBackups": 1, "manageSSL": 1, ' \ '"hostnameSSL": 0, "mailServerSSL": 0 }' UserACL = '{"adminStatus":0, "versionManagement": 1, "createNewUser": 0, "listUsers": 0, "deleteUser": 0 , "resellerCenter": 0, ' \ '"changeUserACL": 0, "createWebsite": 0, "modifyWebsite": 0, "suspendWebsite": 0, "deleteWebsite": 0, ' \ '"createPackage": 0, "listPackages": 0, "deletePackage": 0, "modifyPackage": 0, "createDatabase": 1, "deleteDatabase": 1, ' \ '"listDatabases": 1, "createNameServer": 0, "createDNSZone": 1, "deleteZone": 1, "addDeleteRecords": 1, ' \ '"createEmail": 1, "listEmails": 1, "deleteEmail": 1, "emailForwarding": 1, "changeEmailPassword": 1, ' \ '"dkimManager": 1, "createFTPAccount": 1, "deleteFTPAccount": 1, "listFTPAccounts": 1, "createBackup": 1,' \ ' "restoreBackup": 0, "addDeleteDestinations": 0, "scheduleBackups": 0, "remoteBackups": 0, "googleDriveBackups": 1, "manageSSL": 1, ' \ '"hostnameSSL": 0, "mailServerSSL": 0 }' @staticmethod def FetchCloudLinuxAlmaVersionVersion(): if os.path.exists('/etc/os-release'): data = open('/etc/os-release', 'r').read() if (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and ( data.find('8.9') > -1 or data.find('Anatoly Levchenko') > -1 or data.find('VERSION="8.') > -1): return 'cl-89' elif (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and ( data.find('8.8') > -1 or data.find('Anatoly Filipchenko') > -1): return 'cl-88' elif (data.find('CloudLinux') > -1 or data.find('cloudlinux') > -1) and ( data.find('9.4') > -1 or data.find('VERSION="9.') > -1): return 'cl-88' elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and ( data.find('8.9') > -1 or data.find('Midnight Oncilla') > -1 or data.find('VERSION="8.') > -1): return 'al-88' elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and ( data.find('8.7') > -1 or data.find('Stone Smilodon') > -1): return 'al-87' elif (data.find('AlmaLinux') > -1 or data.find('almalinux') > -1) and ( data.find('9.4') > -1 or data.find('9.3') > -1 or data.find('Shamrock Pampas') > -1 or data.find( 'Seafoam Ocelot') > -1 or data.find('VERSION="9.') > -1): return 'al-93' return None @staticmethod def decideCentosVersion(): if open(Upgrade.CentOSPath, 'r').read().find('CentOS Linux release 8') > -1: return CENTOS8 else: return CENTOS7 @staticmethod def FindOperatingSytem(): if os.path.exists(Upgrade.CentOSPath): result = open(Upgrade.CentOSPath, 'r').read() if result.find('CentOS Linux release 8') > -1 or result.find('CloudLinux release 8') > -1: return CENTOS8 else: return CENTOS7 elif os.path.exists(Upgrade.openEulerPath): result = open(Upgrade.openEulerPath, 'r').read() if result.find('20.03') > -1: return openEuler20 elif result.find('22.03') > -1: return openEuler22 elif os.path.exists(Upgrade.DebianPath): result = open(Upgrade.DebianPath, 'r').read() if result.find('Debian GNU/Linux 11') > -1: return Debian11 elif result.find('Debian GNU/Linux 12') > -1: return Debian12 elif result.find('Debian GNU/Linux 13') > -1: return Debian13 else: return Debian11 # Default to Debian 11 for older versions else: result = open(Upgrade.UbuntuPath, 'r').read() if result.find('20.04') > -1: return Ubuntu20 elif result.find('22.04') > -1: return Ubuntu22 elif result.find('24.04') > -1: return Ubuntu24 else: return Ubuntu18 @staticmethod def stdOut(message, do_exit=0): print("\n\n") print(("[" + time.strftime( "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) print(("[" + time.strftime("%m.%d.%Y_%H-%M-%S") + "] " + message + "\n")) print(("[" + time.strftime( "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) WriteToFile = open(Upgrade.LogPathNew, 'a') WriteToFile.write(("[" + time.strftime( "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) WriteToFile.write(("[" + time.strftime("%m.%d.%Y_%H-%M-%S") + "] " + message + "\n")) WriteToFile.write(("[" + time.strftime( "%m.%d.%Y_%H-%M-%S") + "] #########################################################################\n")) WriteToFile.close() if do_exit: ### remove log file path incase its there if Upgrade.SoftUpgrade: time.sleep(10) if os.path.exists(Upgrade.LogPathNew): os.remove(Upgrade.LogPathNew) if Upgrade.FromCloud == 0: os._exit(0) @staticmethod def executioner(command, component, do_exit=0, shell=False): try: FNULL = open(os.devnull, 'w') count = 0 while True: if shell == False: res = subprocess.call(shlex.split(command), stderr=subprocess.STDOUT) else: res = subprocess.call(command, stderr=subprocess.STDOUT, shell=True) if res != 0: count = count + 1 Upgrade.stdOut(component + ' failed, trying again, try number: ' + str(count), 0) if count == 3: Upgrade.stdOut(component + ' failed.', do_exit) return False else: Upgrade.stdOut(component + ' successful.', 0) break return True except: return False @staticmethod def executioner_silent(command, component, do_exit=0, shell=False): """Silent version of executioner that suppresses all output""" try: FNULL = open(os.devnull, 'w') count = 0 while True: if shell == False: res = subprocess.call(shlex.split(command), stdout=FNULL, stderr=FNULL) else: res = subprocess.call(command, stdout=FNULL, stderr=FNULL, shell=True) if res != 0: count = count + 1 if count == 3: FNULL.close() return False else: FNULL.close() return True except: return False @staticmethod def add_litespeed_repo(): """Add LiteSpeed repository so OpenLiteSpeed 1.8.5+ is available (repo.litespeed.sh).""" return Upgrade.executioner_silent('wget -q -O - https://repo.litespeed.sh | bash', 'LiteSpeed repo', 0, shell=True) @staticmethod def get_installed_ols_version(): """Return installed OpenLiteSpeed version as (major, minor, patch) or None.""" try: for binary in ('/usr/local/lsws/bin/lshttpd', '/usr/local/lsws/bin/openlitespeed'): if not os.path.exists(binary): continue result = subprocess.run( [binary, '-v'], capture_output=True, timeout=5, universal_newlines=True, env=dict(os.environ, PATH=os.environ.get('PATH', '/usr/bin:/bin')) ) out = (result.stdout or '') + (result.stderr or '') m = re.search(r'(\d+)\.(\d+)\.(\d+)', out) if m: return (int(m.group(1)), int(m.group(2)), int(m.group(3))) return None except Exception: return None @staticmethod def updateRepoURL(): command = "sed -i 's|sgp.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/MariaDB.repo" Upgrade.executioner(command, command, 0) command = "sed -i 's|lax.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/MariaDB.repo" Upgrade.executioner(command, command, 0) command = "sed -i 's|fra.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/MariaDB.repo" Upgrade.executioner(command, command, 0) command = "sed -i 's|mirror.cyberpanel.net|cdn.cyberpanel.sh|g' /etc/yum.repos.d/MariaDB.repo" Upgrade.executioner(command, command, 0) command = "sed -i 's|sgp.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/litespeed.repo" Upgrade.executioner(command, command, 0) command = "sed -i 's|lax.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/litespeed.repo" Upgrade.executioner(command, command, 0) command = "sed -i 's|fra.cyberpanel.sh|cdn.cyberpanel.sh|g' /etc/yum.repos.d/litespeed.repo" Upgrade.executioner(command, command, 0) command = "sed -i 's|mirror.cyberpanel.net|cdn.cyberpanel.sh|g' /etc/yum.repos.d/litespeed.repo" Upgrade.executioner(command, command, 0) @staticmethod def mountTemp(): try: if os.path.exists("/usr/.tempdisk"): return 0 command = "dd if=/dev/zero of=/usr/.tempdisk bs=100M count=15" Upgrade.executioner(command, 'mountTemp', 0) command = "mkfs.ext4 -F /usr/.tempdisk" Upgrade.executioner(command, 'mountTemp', 0) command = "mkdir -p /usr/.tmpbak/" Upgrade.executioner(command, 'mountTemp', 0) command = "cp -pr /tmp/* /usr/.tmpbak/" subprocess.call(command, shell=True) command = "mount -o loop,rw,nodev,nosuid,noexec,nofail /usr/.tempdisk /tmp" Upgrade.executioner(command, 'mountTemp', 0) command = "chmod 1777 /tmp" Upgrade.executioner(command, 'mountTemp', 0) command = "cp -pr /usr/.tmpbak/* /tmp/" subprocess.call(command, shell=True) command = "rm -rf /usr/.tmpbak" Upgrade.executioner(command, 'mountTemp', 0) command = "mount --bind /tmp /var/tmp" Upgrade.executioner(command, 'mountTemp', 0) tmp = "/usr/.tempdisk /tmp ext4 loop,rw,noexec,nosuid,nodev,nofail 0 0\n" varTmp = "/tmp /var/tmp none bind 0 0\n" fstab = "/etc/fstab" writeToFile = open(fstab, "a") writeToFile.writelines(tmp) writeToFile.writelines(varTmp) writeToFile.close() except Exception as e: ErrorSanitizer.log_error_securely(e, 'mountTemp') Upgrade.stdOut("Failed to mount temporary filesystem [mountTemp]", 0) @staticmethod def dockerUsers(): ### Docker User/group try: pwd.getpwnam('docker') except KeyError: command = "adduser docker" Upgrade.executioner(command, 'adduser docker', 0) try: grp.getgrnam('docker') except KeyError: command = 'groupadd docker' Upgrade.executioner(command, 'adduser docker', 0) command = 'usermod -aG docker docker' Upgrade.executioner(command, 'adduser docker', 0) command = 'usermod -aG docker cyberpanel' Upgrade.executioner(command, 'adduser docker', 0) ### @staticmethod def fixSudoers(): try: distroPath = '/etc/lsb-release' if os.path.exists(distroPath): fileName = '/etc/sudoers' data = open(fileName, 'r').readlines() writeDataToFile = open(fileName, 'w') for line in data: if line.find("%sudo ALL=(ALL:ALL)") > -1: continue else: writeDataToFile.write(line) writeDataToFile.close() else: try: path = "/etc/sudoers" data = open(path, 'r').readlines() writeToFile = open(path, 'w') for items in data: if items.find("wheel") > -1 and items.find("ALL=(ALL)"): continue elif items.find("root") > -1 and items.find("ALL=(ALL)") > -1 and items[0] != '#': writeToFile.writelines('root ALL=(ALL:ALL) ALL\n') else: writeToFile.writelines(items) writeToFile.close() except: pass command = "chsh -s /bin/false cyberpanel" Upgrade.executioner(command, 0) except IOError as err: pass @staticmethod def detectArchitecture(): """Detect system architecture - custom binaries only for x86_64""" try: import platform arch = platform.machine() return arch == "x86_64" except Exception as msg: Upgrade.stdOut(str(msg) + " [detectArchitecture]", 0) return False @staticmethod def detectPlatform(): """Detect OS platform for binary selection (rhel8, rhel9, ubuntu)""" try: # Check for Ubuntu if os.path.exists('/etc/lsb-release'): with open('/etc/lsb-release', 'r') as f: content = f.read() if 'Ubuntu' in content or 'ubuntu' in content: return 'ubuntu' # Check for RHEL-based distributions if os.path.exists('/etc/os-release'): with open('/etc/os-release', 'r') as f: content = f.read().lower() # Check for version 8.x (RHEL, AlmaLinux, Rocky, CloudLinux, CentOS 8) if 'version="8.' in content or 'version_id="8.' in content: if any(distro in content for distro in ['red hat', 'almalinux', 'rocky', 'cloudlinux', 'centos']): return 'rhel8' # Check for version 9.x if 'version="9.' in content or 'version_id="9.' in content: if any(distro in content for distro in ['red hat', 'almalinux', 'rocky', 'cloudlinux', 'centos']): return 'rhel9' # Default to rhel8 if can't detect (safer default - rhel9 binaries may require GLIBC 2.35) Upgrade.stdOut("WARNING: Could not detect platform, defaulting to rhel8", 0) return 'rhel8' except Exception as msg: Upgrade.stdOut(f"ERROR detecting platform: {msg}, defaulting to rhel8", 0) return 'rhel8' @staticmethod def getSystemGLIBCVersion(): """Get the system's GLIBC version""" try: import subprocess # Try to get GLIBC version from ldd result = subprocess.run(['ldd', '--version'], capture_output=True, text=True, timeout=5) if result.returncode == 0: # ldd --version output format: "ldd (GNU libc) 2.34" for line in result.stdout.split('\n'): if 'GNU libc' in line or 'glibc' in line.lower(): import re version_match = re.search(r'(\d+)\.(\d+)', line) if version_match: major = int(version_match.group(1)) minor = int(version_match.group(2)) return (major, minor) # Fallback: try to read from libc.so.6 try: result = subprocess.run(['/lib64/libc.so.6'], capture_output=True, text=True, timeout=5) if result.returncode != 0 and 'version' in result.stderr.lower(): import re version_match = re.search(r'(\d+)\.(\d+)', result.stderr) if version_match: major = int(version_match.group(1)) minor = int(version_match.group(2)) return (major, minor) except: pass # If we can't detect, assume a safe minimum Upgrade.stdOut("WARNING: Could not detect GLIBC version, assuming 2.34", 0) return (2, 34) except Exception as msg: Upgrade.stdOut(f"WARNING: Error detecting GLIBC version: {msg}, assuming 2.34", 0) return (2, 34) @staticmethod def checkBinaryGLIBCRequirements(binary_path): """Check GLIBC version requirements of a binary file""" try: import subprocess import re # Use objdump to check GLIBC version requirements # objdump -T shows dynamic symbols and their GLIBC version requirements result = subprocess.run(['objdump', '-T', binary_path], capture_output=True, text=True, timeout=10) if result.returncode != 0: # objdump might not be available, try readelf result = subprocess.run(['readelf', '-d', binary_path], capture_output=True, text=True, timeout=10) if result.returncode != 0: Upgrade.stdOut("WARNING: Could not check binary GLIBC requirements (objdump/readelf not available)", 0) return None # Look for GLIBC version requirements in the output # Format: GLIBC_2.35, GLIBC_2.34, etc. max_version = None for line in result.stdout.split('\n') + result.stderr.split('\n'): # Look for GLIBC version symbols matches = re.findall(r'GLIBC_(\d+)\.(\d+)', line) for match in matches: major = int(match[0]) minor = int(match[1]) if max_version is None or (major, minor) > max_version: max_version = (major, minor) return max_version except FileNotFoundError: # objdump/readelf not available Upgrade.stdOut("WARNING: objdump/readelf not available, skipping GLIBC check", 0) return None except Exception as msg: Upgrade.stdOut(f"WARNING: Error checking binary GLIBC requirements: {msg}", 0) return None @staticmethod def verifyBinaryCompatibility(binary_path): """Verify that a binary is compatible with the system's GLIBC version""" try: system_glibc = Upgrade.getSystemGLIBCVersion() binary_glibc = Upgrade.checkBinaryGLIBCRequirements(binary_path) if binary_glibc is None: # Can't check, but we can try a test run Upgrade.stdOut("Cannot verify GLIBC requirements, performing test run...", 0) return Upgrade.testBinaryExecution(binary_path) Upgrade.stdOut(f"System GLIBC: {system_glibc[0]}.{system_glibc[1]}", 0) Upgrade.stdOut(f"Binary requires GLIBC: {binary_glibc[0]}.{binary_glibc[1]}", 0) # Check if binary requires newer GLIBC than system has if binary_glibc > system_glibc: Upgrade.stdOut(f"ERROR: Binary requires GLIBC {binary_glibc[0]}.{binary_glibc[1]}, but system has {system_glibc[0]}.{system_glibc[1]}", 0) return False Upgrade.stdOut("GLIBC compatibility check passed", 0) return True except Exception as msg: Upgrade.stdOut(f"WARNING: Error verifying binary compatibility: {msg}", 0) # If we can't verify, try test execution return Upgrade.testBinaryExecution(binary_path) @staticmethod def testBinaryExecution(binary_path): """Test if binary can execute (checks GLIBC compatibility indirectly)""" try: import subprocess # Try to run the binary with --version or -v flag # This will fail immediately if GLIBC is incompatible # The error format is: "./binary: /lib64/libc.so.6: version `GLIBC_X.Y' not found" result = subprocess.run([binary_path, '--version'], capture_output=True, text=True, timeout=5) # Check both stdout and stderr for GLIBC errors output = result.stdout + result.stderr # Look for GLIBC version not found errors if 'GLIBC' in output and ('not found' in output or 'version' in output.lower()): # Extract the required GLIBC version from error message import re glibc_match = re.search(r"GLIBC_(\d+)\.(\d+)'?\s+not found", output) if glibc_match: required_major = int(glibc_match.group(1)) required_minor = int(glibc_match.group(2)) Upgrade.stdOut(f"ERROR: Binary requires GLIBC {required_major}.{required_minor} which is not available", 0) else: Upgrade.stdOut(f"ERROR: Binary GLIBC compatibility test failed: {output[:200]}", 0) return False # If binary executed (even with non-zero return code for --version), it's compatible if result.returncode == 0 or len(result.stdout) > 0: return True # If we get here, binary might not support --version, try -v result = subprocess.run([binary_path, '-v'], capture_output=True, text=True, timeout=5) output = result.stdout + result.stderr if 'GLIBC' in output and ('not found' in output or 'version' in output.lower()): import re glibc_match = re.search(r"GLIBC_(\d+)\.(\d+)'?\s+not found", output) if glibc_match: required_major = int(glibc_match.group(1)) required_minor = int(glibc_match.group(2)) Upgrade.stdOut(f"ERROR: Binary requires GLIBC {required_major}.{required_minor} which is not available", 0) else: Upgrade.stdOut(f"ERROR: Binary GLIBC compatibility test failed: {output[:200]}", 0) return False # If no GLIBC error and we got some output, assume compatible if len(output) > 0: return True # If binary doesn't support --version/-v, try to check if it's executable # by checking file type try: result = subprocess.run(['file', binary_path], capture_output=True, text=True, timeout=5) if 'ELF' in result.stdout and 'executable' in result.stdout: Upgrade.stdOut("WARNING: Cannot test binary execution, but file appears valid", 0) return True except: pass # Conservative approach: if we can't verify, assume incompatible to be safe Upgrade.stdOut("WARNING: Could not verify binary execution, skipping installation for safety", 0) return False except subprocess.TimeoutExpired: Upgrade.stdOut("WARNING: Binary test timed out, assuming compatible", 0) return True except FileNotFoundError as e: # Binary file not found or command not found Upgrade.stdOut(f"ERROR: Binary test failed - file or command not found: {e}", 0) return False except Exception as msg: # Check if it's a GLIBC error in the exception itself error_str = str(msg) if 'GLIBC' in error_str and 'not found' in error_str: Upgrade.stdOut(f"ERROR: Binary GLIBC compatibility test failed: {error_str}", 0) return False Upgrade.stdOut(f"WARNING: Could not test binary execution: {msg}", 0) # Conservative approach: if we can't test, assume incompatible to be safe return False @staticmethod def downloadCustomBinary(url, destination, expected_sha256=None): """Download custom binary file with optional checksum verification""" try: Upgrade.stdOut(f"Downloading {os.path.basename(destination)}...", 0) # Use wget for better progress display command = f'wget -q --show-progress {url} -O {destination}' res = subprocess.call(shlex.split(command)) # Check if file was downloaded successfully by verifying it exists and has reasonable size if os.path.exists(destination): file_size = os.path.getsize(destination) # Verify file size is reasonable (at least 10KB to avoid error pages/empty files) if file_size > 10240: # 10KB if file_size > 1048576: # 1MB Upgrade.stdOut(f"Downloaded successfully ({file_size / (1024*1024):.2f} MB)", 0) else: Upgrade.stdOut(f"Downloaded successfully ({file_size / 1024:.2f} KB)", 0) # Verify checksum if provided if expected_sha256: Upgrade.stdOut("Verifying checksum...", 0) import hashlib sha256_hash = hashlib.sha256() with open(destination, "rb") as f: for byte_block in iter(lambda: f.read(4096), b""): sha256_hash.update(byte_block) actual_sha256 = sha256_hash.hexdigest() if actual_sha256 == expected_sha256: Upgrade.stdOut("Checksum verified successfully", 0) return True else: Upgrade.stdOut(f"ERROR: Checksum mismatch!", 0) Upgrade.stdOut(f"Expected: {expected_sha256}", 0) Upgrade.stdOut(f"Got: {actual_sha256}", 0) return False else: return True else: Upgrade.stdOut(f"ERROR: Downloaded file too small ({file_size} bytes)", 0) return False else: Upgrade.stdOut("ERROR: Download failed - file not found", 0) return False except Exception as msg: Upgrade.stdOut(f"ERROR: {msg} [downloadCustomBinary]", 0) return False @staticmethod def installCustomOLSBinaries(): """Install custom OpenLiteSpeed binaries with PHP config support""" try: Upgrade.stdOut("Installing Custom OpenLiteSpeed Binaries", 0) Upgrade.stdOut("=" * 50, 0) # Check architecture if not Upgrade.detectArchitecture(): Upgrade.stdOut("WARNING: Custom binaries only available for x86_64", 0) Upgrade.stdOut("Skipping custom binary installation", 0) Upgrade.stdOut("Standard OLS will be used", 0) return True # Not a failure, just skip # Detect platform platform = Upgrade.detectPlatform() Upgrade.stdOut(f"Detected platform: {platform}", 0) config = ols_binaries_config.BINARY_CONFIGS.get(platform) if not config: Upgrade.stdOut(f"ERROR: No binaries available for platform {platform}", 0) Upgrade.stdOut("Skipping custom binary installation", 0) return True # Not fatal OLS_BINARY_URL = config['url'] OLS_BINARY_SHA256 = config['sha256'] MODULE_URL = config['module_url'] MODULE_SHA256 = config['module_sha256'] MODSEC_URL = config.get('modsec_url') MODSEC_SHA256 = config.get('modsec_sha256') OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed" MODULE_PATH = "/usr/local/lsws/modules/cyberpanel_ols.so" MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so" # Create backup from datetime import datetime timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") backup_dir = f"/usr/local/lsws/backup-{timestamp}" try: os.makedirs(backup_dir, exist_ok=True) if os.path.exists(OLS_BINARY_PATH): shutil.copy2(OLS_BINARY_PATH, f"{backup_dir}/openlitespeed.backup") Upgrade.stdOut(f"Backup created at: {backup_dir}", 0) # Also backup existing ModSecurity if it exists if os.path.exists(MODSEC_PATH): shutil.copy2(MODSEC_PATH, f"{backup_dir}/mod_security.so.backup") except Exception as e: Upgrade.stdOut(f"WARNING: Could not create backup: {e}", 0) # Download binaries to temp location tmp_binary = "/tmp/openlitespeed-custom" tmp_module = "/tmp/cyberpanel_ols.so" tmp_modsec = "/tmp/mod_security.so" Upgrade.stdOut("Downloading custom binaries...", 0) # Download OpenLiteSpeed binary with checksum verification if not Upgrade.downloadCustomBinary(OLS_BINARY_URL, tmp_binary, OLS_BINARY_SHA256): Upgrade.stdOut("ERROR: Failed to download or verify OLS binary", 0) Upgrade.stdOut("Continuing with standard OLS", 0) return True # Not fatal, continue with standard OLS # CRITICAL: Verify GLIBC compatibility before installation Upgrade.stdOut("Verifying GLIBC compatibility...", 0) if not Upgrade.verifyBinaryCompatibility(tmp_binary): Upgrade.stdOut("=" * 50, 0) Upgrade.stdOut("ERROR: Binary GLIBC requirements incompatible with system", 0) Upgrade.stdOut("This binary would cause OpenLiteSpeed to fail to start", 0) Upgrade.stdOut("Skipping custom binary installation to preserve system stability", 0) Upgrade.stdOut("Standard OLS binary from package manager will be used", 0) Upgrade.stdOut("=" * 50, 0) # Clean up downloaded binary try: if os.path.exists(tmp_binary): os.remove(tmp_binary) except: pass return True # Not fatal, continue with standard OLS # Download module with checksum verification (if available) module_downloaded = False if MODULE_URL and MODULE_SHA256: if not Upgrade.downloadCustomBinary(MODULE_URL, tmp_module, MODULE_SHA256): Upgrade.stdOut("ERROR: Failed to download or verify module", 0) Upgrade.stdOut("Continuing with standard OLS", 0) return True # Not fatal, continue with standard OLS module_downloaded = True else: Upgrade.stdOut("Note: No CyberPanel module for this platform", 0) # Download compatible ModSecurity if existing ModSecurity is installed # This prevents ABI incompatibility crashes (Signal 11/SIGSEGV) modsec_downloaded = False if os.path.exists(MODSEC_PATH) and MODSEC_URL and MODSEC_SHA256: Upgrade.stdOut("Existing ModSecurity detected - downloading compatible version...", 0) if Upgrade.downloadCustomBinary(MODSEC_URL, tmp_modsec, MODSEC_SHA256): modsec_downloaded = True else: Upgrade.stdOut("WARNING: Failed to download compatible ModSecurity", 0) Upgrade.stdOut("ModSecurity may crash due to ABI incompatibility", 0) Upgrade.stdOut("Consider manually updating ModSecurity after upgrade", 0) # Install OpenLiteSpeed binary Upgrade.stdOut("Installing custom binaries...", 0) try: # Make binary executable before moving os.chmod(tmp_binary, 0o755) # Final compatibility test before installation if not Upgrade.testBinaryExecution(tmp_binary): Upgrade.stdOut("ERROR: Final binary compatibility test failed", 0) Upgrade.stdOut("Skipping installation to prevent OpenLiteSpeed failure", 0) try: if os.path.exists(tmp_binary): os.remove(tmp_binary) except: pass return True # Not fatal, continue with standard OLS shutil.move(tmp_binary, OLS_BINARY_PATH) Upgrade.stdOut("Installed OpenLiteSpeed binary", 0) except Exception as e: Upgrade.stdOut(f"ERROR: Failed to install binary: {e}", 0) # Try to restore backup if installation failed try: if os.path.exists(f"{backup_dir}/openlitespeed.backup"): shutil.copy2(f"{backup_dir}/openlitespeed.backup", OLS_BINARY_PATH) Upgrade.stdOut("Restored original binary from backup", 0) except: pass return False # Install module (if downloaded) if module_downloaded: try: os.makedirs(os.path.dirname(MODULE_PATH), exist_ok=True) shutil.move(tmp_module, MODULE_PATH) os.chmod(MODULE_PATH, 0o644) Upgrade.stdOut("Installed CyberPanel module", 0) except Exception as e: Upgrade.stdOut(f"ERROR: Failed to install module: {e}", 0) return False # Install compatible ModSecurity (if downloaded) if modsec_downloaded: try: shutil.move(tmp_modsec, MODSEC_PATH) os.chmod(MODSEC_PATH, 0o644) Upgrade.stdOut("Installed compatible ModSecurity module", 0) except Exception as e: Upgrade.stdOut(f"WARNING: Failed to install ModSecurity: {e}", 0) # Non-fatal, continue # Verify installation if os.path.exists(OLS_BINARY_PATH): if not module_downloaded or os.path.exists(MODULE_PATH): Upgrade.stdOut("=" * 50, 0) Upgrade.stdOut("Custom Binaries Installed Successfully", 0) Upgrade.stdOut("Features enabled:", 0) Upgrade.stdOut(" - Static-linked cross-platform binary", 0) if module_downloaded: Upgrade.stdOut(" - Apache-style .htaccess support", 0) Upgrade.stdOut(" - php_value/php_flag directives", 0) Upgrade.stdOut(" - Enhanced header control", 0) Upgrade.stdOut(f"Backup: {backup_dir}", 0) Upgrade.stdOut("=" * 50, 0) # Configure module after installation Upgrade.configureCustomModule() # Enable Auto-SSL if not already configured conf_path = '/usr/local/lsws/conf/httpd_config.conf' try: with open(conf_path, 'r') as f: content = f.read() if 'autoSSL' not in content: content = re.sub( r'(adminEmails\s+\S+)', r'\1\nautoSSL 1\nacmeEmail admin@cyberpanel.net', content, count=1 ) with open(conf_path, 'w') as f: f.write(content) Upgrade.stdOut("Auto-SSL enabled in httpd_config.conf", 0) except Exception as e: Upgrade.stdOut(f"WARNING: Could not enable Auto-SSL: {e}", 0) return True Upgrade.stdOut("ERROR: Installation verification failed", 0) return False except Exception as msg: Upgrade.stdOut(f"ERROR: {msg} [installCustomOLSBinaries]", 0) Upgrade.stdOut("Continuing with standard OLS", 0) return True # Non-fatal error, continue @staticmethod def configureCustomModule(): """Configure CyberPanel module in OpenLiteSpeed config""" try: Upgrade.stdOut("Configuring CyberPanel module...", 0) CONFIG_FILE = "/usr/local/lsws/conf/httpd_config.conf" if not os.path.exists(CONFIG_FILE): Upgrade.stdOut("WARNING: Config file not found", 0) Upgrade.stdOut("Module will be auto-loaded", 0) return True # Check if module is already configured with open(CONFIG_FILE, 'r') as f: content = f.read() if 'cyberpanel_ols' in content: Upgrade.stdOut("Module already configured", 0) return True # Add module configuration module_config = """ module cyberpanel_ols { ls_enabled 1 } """ # Backup config shutil.copy2(CONFIG_FILE, f"{CONFIG_FILE}.backup") # Append module config with open(CONFIG_FILE, 'a') as f: f.write(module_config) Upgrade.stdOut("Module configured successfully", 0) return True except Exception as msg: Upgrade.stdOut(f"WARNING: Module configuration failed: {msg}", 0) Upgrade.stdOut("Module may still work via auto-load", 0) return True # Non-fatal @staticmethod def download_install_phpmyadmin(): try: cwd = os.getcwd() if not os.path.exists("/usr/local/CyberCP/public"): os.mkdir("/usr/local/CyberCP/public") try: shutil.rmtree("/usr/local/CyberCP/public/phpmyadmin") except: pass # Try to fetch latest phpMyAdmin version from GitHub phpmyadmin_version = '5.2.3' # Fallback version try: from plogical.versionFetcher import get_latest_phpmyadmin_version latest_version = get_latest_phpmyadmin_version() if latest_version and latest_version != phpmyadmin_version: Upgrade.stdOut(f"Using latest phpMyAdmin version: {latest_version}", 0) phpmyadmin_version = latest_version else: Upgrade.stdOut(f"Using fallback phpMyAdmin version: {phpmyadmin_version}", 0) except Exception as e: Upgrade.stdOut(f"Failed to fetch latest phpMyAdmin version, using fallback: {e}", 0) Upgrade.stdOut("Installing phpMyAdmin...", 0) tarball = '/usr/local/CyberCP/public/phpmyadmin.tar.gz' command = f'wget -q -O {tarball} https://files.phpmyadmin.net/phpMyAdmin/{phpmyadmin_version}/phpMyAdmin-{phpmyadmin_version}-all-languages.tar.gz' Upgrade.executioner_silent(command, f'Download phpMyAdmin {phpmyadmin_version}') if not os.path.isfile(tarball) or os.path.getsize(tarball) < 1000000: raise RuntimeError('phpMyAdmin download failed or file too small (check files.phpmyadmin.net)') command = 'tar -xzf /usr/local/CyberCP/public/phpmyadmin.tar.gz -C /usr/local/CyberCP/public/' Upgrade.executioner_silent(command, 'Extract phpMyAdmin') # Move extracted dir to phpmyadmin (support phpMyAdmin-X.Y.Z-all-languages or similar) import glob extracted = glob.glob('/usr/local/CyberCP/public/phpMyAdmin-*-all-languages') if not extracted: extracted = glob.glob('/usr/local/CyberCP/public/phpMyAdmin-*') if extracted: if os.path.exists('/usr/local/CyberCP/public/phpmyadmin'): shutil.rmtree('/usr/local/CyberCP/public/phpmyadmin') os.rename(extracted[0], '/usr/local/CyberCP/public/phpmyadmin') else: Upgrade.executioner('mv /usr/local/CyberCP/public/phpMyAdmin-*-all-languages /usr/local/CyberCP/public/phpmyadmin', 0) command = 'rm -f /usr/local/CyberCP/public/phpmyadmin.tar.gz' Upgrade.executioner_silent(command, 'Cleanup phpMyAdmin tar.gz') if not os.path.isdir('/usr/local/CyberCP/public/phpmyadmin'): raise RuntimeError('phpMyAdmin directory was not created after extract/mv') Upgrade.stdOut("phpMyAdmin installation completed.", 0) ## Write secret phrase rString = ''.join([random.choice(string.ascii_letters + string.digits) for n in range(32)]) data = open('/usr/local/CyberCP/public/phpmyadmin/config.sample.inc.php', 'r').readlines() writeToFile = open('/usr/local/CyberCP/public/phpmyadmin/config.inc.php', 'w') writeE = 1 phpMyAdminContent = """ $cfg['Servers'][$i]['AllowNoPassword'] = false; $cfg['Servers'][$i]['auth_type'] = 'signon'; $cfg['Servers'][$i]['SignonSession'] = 'SignonSession'; $cfg['Servers'][$i]['SignonURL'] = 'phpmyadminsignin.php'; $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout'; $cfg['Servers'][$i]['host'] = '127.0.0.1'; $cfg['Servers'][$i]['port'] = '3306'; """ for items in data: if items.find('blowfish_secret') > -1: writeToFile.writelines( "$cfg['blowfish_secret'] = '" + rString + "'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */\n") elif items.find('/* Authentication type */') > -1: writeToFile.writelines(items) writeToFile.write(phpMyAdminContent) writeE = 0 elif items.find("$cfg['Servers'][$i]['AllowNoPassword']") > -1: writeE = 1 else: if writeE: writeToFile.writelines(items) writeToFile.writelines("$cfg['TempDir'] = '/usr/local/CyberCP/public/phpmyadmin/tmp';\n") writeToFile.close() os.mkdir('/usr/local/CyberCP/public/phpmyadmin/tmp') command = 'cp /usr/local/CyberCP/plogical/phpmyadminsignin.php /usr/local/CyberCP/public/phpmyadmin/phpmyadminsignin.php' Upgrade.executioner(command, 0) passFile = "/etc/cyberpanel/mysqlPassword" try: import json jsonData = json.loads(open(passFile, 'r').read()) mysqluser = jsonData['mysqluser'] mysqlpassword = jsonData['mysqlpassword'] mysqlport = jsonData.get('mysqlport', 3306) mysqlhost = jsonData.get('mysqlhost', '127.0.0.1') or '127.0.0.1' if mysqlhost == 'localhost': mysqlhost = '127.0.0.1' command = "sed -i 's|localhost|%s|g' /usr/local/CyberCP/public/phpmyadmin/phpmyadminsignin.php" % ( mysqlhost) Upgrade.executioner(command, 0) except: pass command = 'chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin' Upgrade.executioner_silent(command, 'chown phpMyAdmin') command = 'chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/tmp' Upgrade.executioner_silent(command, 'chown phpMyAdmin tmp') os.chdir(cwd) except Exception as e: ErrorSanitizer.log_error_securely(e, 'download_install_phpmyadmin') Upgrade.stdOut("Failed to download and install phpMyAdmin [download_install_phpmyadmin]", 0) @staticmethod def setupComposer(): composer_sh = '/tmp/composer.sh' try: if os.path.exists(composer_sh): os.remove(composer_sh) # Download to known path so chmod/run work regardless of cwd command = "wget -q https://cyberpanel.sh/composer.sh -O " + composer_sh Upgrade.executioner(command, 0) if not os.path.isfile(composer_sh): command = "curl -sSL https://cyberpanel.sh/composer.sh -o " + composer_sh Upgrade.executioner(command, 0) if not os.path.isfile(composer_sh): Upgrade.stdOut("composer.sh download failed, skipping", 0) return command = "chmod +x " + composer_sh Upgrade.executioner(command, 0) command = "bash " + composer_sh Upgrade.executioner(command, 0) except Exception as e: ErrorSanitizer.log_error_securely(e, 'setupComposer') Upgrade.stdOut("setupComposer error (non-fatal)", 0) @staticmethod def downoad_and_install_raindloop(): try: ####### # if os.path.exists("/usr/local/CyberCP/public/rainloop"): # # if os.path.exists("/usr/local/lscp/cyberpanel/rainloop/data"): # pass # else: # command = "mv /usr/local/CyberCP/public/rainloop/data /usr/local/lscp/cyberpanel/rainloop/data" # Upgrade.executioner(command, 0) # # command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/rainloop/data" # Upgrade.executioner(command, 0) # # iPath = os.listdir('/usr/local/CyberCP/public/rainloop/rainloop/v/') # # path = "/usr/local/CyberCP/public/snappymail/snappymail/v/%s/include.php" % (iPath[0]) # # data = open(path, 'r').readlines() # writeToFile = open(path, 'w') # # for items in data: # if items.find("$sCustomDataPath = '';") > -1: # writeToFile.writelines( # " $sCustomDataPath = '/usr/local/lscp/cyberpanel/rainloop/data';\n") # else: # writeToFile.writelines(items) # # writeToFile.close() # return 0 cwd = os.getcwd() if not os.path.exists("/usr/local/CyberCP/public"): os.mkdir("/usr/local/CyberCP/public") # Try to fetch latest SnappyMail version from GitHub try: from plogical.versionFetcher import get_latest_snappymail_version latest_version = get_latest_snappymail_version() if latest_version and latest_version != Upgrade.SnappyVersion: Upgrade.stdOut(f"Using latest SnappyMail version: {latest_version}", 0) Upgrade.SnappyVersion = latest_version else: Upgrade.stdOut(f"Using fallback SnappyMail version: {Upgrade.SnappyVersion}", 0) except Exception as e: Upgrade.stdOut(f"Failed to fetch latest SnappyMail version, using fallback: {e}", 0) os.chdir("/usr/local/CyberCP/public") count = 1 Upgrade.stdOut("Installing SnappyMail...", 0) while (1): command = 'wget -q https://github.com/the-djmaze/snappymail/releases/download/v%s/snappymail-%s.zip' % ( Upgrade.SnappyVersion, Upgrade.SnappyVersion) cmd = shlex.split(command) res = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if res != 0: count = count + 1 if count == 3: break else: break ############# count = 0 if os.path.exists('/usr/local/CyberCP/public/snappymail'): shutil.rmtree('/usr/local/CyberCP/public/snappymail') while (1): command = 'unzip -q snappymail-%s.zip -d /usr/local/CyberCP/public/snappymail' % (Upgrade.SnappyVersion) cmd = shlex.split(command) res = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if res != 0: count = count + 1 if count == 3: break else: break try: os.remove("snappymail-%s.zip" % (Upgrade.SnappyVersion)) except: pass ####### os.chdir("/usr/local/CyberCP/public/snappymail") count = 0 while (1): command = 'find . -type d -exec chmod 755 {} \;' cmd = shlex.split(command) res = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if res != 0: count = count + 1 if count == 3: break else: break ############# count = 0 while (1): command = 'find . -type f -exec chmod 644 {} \;' cmd = shlex.split(command) res = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if res != 0: count = count + 1 if count == 3: break else: break ###### iPath = os.listdir('/usr/local/CyberCP/public/snappymail/snappymail/v/') path = "/usr/local/CyberCP/public/snappymail/snappymail/v/%s/include.php" % (iPath[0]) data = open(path, 'r').readlines() writeToFile = open(path, 'w') for items in data: if items.find("$sCustomDataPath = '';") > -1: writeToFile.writelines( " $sCustomDataPath = '/usr/local/lscp/cyberpanel/snappymail/data';\n") else: writeToFile.writelines(items) writeToFile.close() # Create snappymail data directories (rainloop is deprecated in 2.5.5) command = "mkdir -p /usr/local/lscp/cyberpanel/snappymail/data/_data_/_default_/configs/" Upgrade.executioner_silent(command, 'mkdir snappymail configs', 0) command = f'wget -q -O /usr/local/CyberCP/snappymail_cyberpanel.php https://raw.githubusercontent.com/the-djmaze/snappymail/master/integrations/cyberpanel/install.php' Upgrade.executioner_silent(command, 'verify certificate', 0) command = f'/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/snappymail_cyberpanel.php' Upgrade.executioner_silent(command, 'verify certificate', 0) # labsPath = '/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/application.ini' # labsData = """[labs] # imap_folder_list_limit = 0 # autocreate_system_folders = On # """ # # writeToFile = open(labsPath, 'a') # writeToFile.write(labsData) # writeToFile.close() includeFileOldPath = '/usr/local/CyberCP/public/snappymail/_include.php' includeFileNewPath = '/usr/local/CyberCP/public/snappymail/include.php' # if os.path.exists(includeFileOldPath): # writeToFile = open(includeFileOldPath, 'a') # writeToFile.write("\ndefine('APP_DATA_FOLDER_PATH', '/usr/local/lscp/cyberpanel/rainloop/data/');\n") # writeToFile.close() # command = 'mv %s %s' % (includeFileOldPath, includeFileNewPath) # Upgrade.executioner(command, 'mkdir snappymail configs', 0) ## take care of auto create folders ## Disable local cert verification # command = "sed -i 's|verify_certificate = On|verify_certificate = Off|g' %s" % (labsPath) # Upgrade.executioner(command, 'verify certificate', 0) # labsData = open(labsPath, 'r').read() # labsDataLines = open(labsPath, 'r').readlines() # # if labsData.find('autocreate_system_folders') > -1: # command = "sed -i 's|autocreate_system_folders = Off|autocreate_system_folders = On|g' %s" % (labsPath) # Upgrade.executioner(command, 'mkdir snappymail configs', 0) # else: # WriteToFile = open(labsPath, 'w') # for lines in labsDataLines: # if lines.find('[labs]') > -1: # WriteToFile.write(lines) # WriteToFile.write(f'autocreate_system_folders = On\n') # else: # WriteToFile.write(lines) # WriteToFile.close() ##take care of imap_folder_list_limit # labsDataLines = open(labsPath, 'r').readlines() # # if labsData.find('imap_folder_list_limit') == -1: # WriteToFile = open(labsPath, 'w') # for lines in labsDataLines: # if lines.find('[labs]') > -1: # WriteToFile.write(lines) # WriteToFile.write(f'imap_folder_list_limit = 0\n') # else: # WriteToFile.write(lines) # WriteToFile.close() ### now download and install actual plugin # command = f'mkdir /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect' # Upgrade.executioner(command, 'verify certificate', 0) # # command = f'chmod 700 /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect' # Upgrade.executioner(command, 'verify certificate', 0) # # command = f'chown lscpd:lscpd /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect' # Upgrade.executioner(command, 'verify certificate', 0) # # command = f'wget -O /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect/index.php https://raw.githubusercontent.com/the-djmaze/snappymail/master/plugins/mailbox-detect/index.php' # Upgrade.executioner(command, 'verify certificate', 0) # # command = f'chmod 644 /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect/index.php' # Upgrade.executioner(command, 'verify certificate', 0) # # command = f'chown lscpd:lscpd /usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/plugins/mailbox-detect/index.php' # Upgrade.executioner(command, 'verify certificate', 0) # # ### Enable plugins and enable mailbox creation plugin # # labsDataLines = open(labsPath, 'r').readlines() # PluginsActivator = 0 # WriteToFile = open(labsPath, 'w') # # # for lines in labsDataLines: # if lines.find('[plugins]') > -1: # PluginsActivator = 1 # WriteToFile.write(lines) # elif PluginsActivator and lines.find('enable = ') > -1: # WriteToFile.write(f'enable = On\n') # elif PluginsActivator and lines.find('enabled_list = ') > -1: # WriteToFile.write(f'enabled_list = "mailbox-detect"\n') # elif PluginsActivator == 1 and lines.find('[defaults]') > -1: # PluginsActivator = 0 # WriteToFile.write(lines) # else: # WriteToFile.write(lines) # WriteToFile.close() # # ## enable auto create in the enabled plugin # PluginsFilePath = '/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/plugin-mailbox-detect.json' # # WriteToFile = open(PluginsFilePath, 'w') # WriteToFile.write("""{ # "plugin": { # "autocreate_system_folders": true # } # } # """) # WriteToFile.close() # # command = f'chown lscpd:lscpd {PluginsFilePath}' # Upgrade.executioner(command, 'verify certificate', 0) # # command = f'chmod 600 {PluginsFilePath}' # Upgrade.executioner(command, 'verify certificate', 0) os.chdir(cwd) # Migrate data from old rainloop folder to new snappymail folder (2.4.4 -> 2.5.5 upgrade) Upgrade.migrateRainloopToSnappymail() Upgrade.stdOut("SnappyMail installation completed.", 0) except Exception as e: ErrorSanitizer.log_error_securely(e, 'downoad_and_install_raindloop') Upgrade.stdOut("Failed to download and install Rainloop [downoad_and_install_raindloop]", 0) return 1 @staticmethod def downloadLink(): try: version_number = VERSION version_build = str(BUILD) try: Content = {"version":version_number,"build":version_build} path = "/usr/local/CyberCP/version.txt" writeToFile = open(path, 'w') writeToFile.write(json.dumps(Content)) writeToFile.close() except: pass return (version_number + "." + version_build + ".tar.gz") except Exception as e: ErrorSanitizer.log_error_securely(e, 'downloadLink') Upgrade.stdOut("Failed to download required files [downloadLink]") os._exit(0) @staticmethod def setupCLI(): try: command = "ln -s /usr/local/CyberCP/cli/cyberPanel.py /usr/bin/cyberpanel" Upgrade.executioner(command, 'CLI Symlink', 0) command = "chmod +x /usr/local/CyberCP/cli/cyberPanel.py" Upgrade.executioner(command, 'CLI Permissions', 0) except OSError as e: ErrorSanitizer.log_error_securely(e, 'setupCLI') Upgrade.stdOut("Failed to setup CLI [setupCLI]") return 0 @staticmethod def downloadCDNLibraries(): """ Download CDN libraries (qrious, chart.js) locally to eliminate tracking prevention warnings. These files are downloaded before collectstatic runs so they're included in the static files. Tries latest version first, falls back to hardcoded version if latest fails. """ try: custom_js_dir = '/usr/local/CyberCP/baseTemplate/static/baseTemplate/custom-js' # Ensure directory exists if not os.path.exists(custom_js_dir): os.makedirs(custom_js_dir, mode=0o755) # Download qrious.min.js - try latest first, fallback to known working version qrious_path = os.path.join(custom_js_dir, 'qrious.min.js') qrious_urls = [ 'https://cdn.jsdelivr.net/npm/qrious@latest/dist/qrious.min.js', # Try latest first 'https://cdn.jsdelivr.net/npm/qrious@4.0.2/dist/qrious.min.js' # Fallback to known working version ] qrious_downloaded = False for qrious_url in qrious_urls: command = f'wget -q --timeout=30 {qrious_url} -O {qrious_path}' result = subprocess.call(shlex.split(command)) if result == 0 and os.path.exists(qrious_path) and os.path.getsize(qrious_path) > 1000: # At least 1KB os.chmod(qrious_path, 0o644) version_info = "latest" if "latest" in qrious_url else "4.0.2" Upgrade.stdOut(f"Downloaded qrious.min.js ({version_info})", 0) qrious_downloaded = True break if not qrious_downloaded: Upgrade.stdOut("Warning: Failed to download qrious.min.js, continuing anyway", 0) # Download chart.js - try latest first, fallback to known working version chartjs_path = os.path.join(custom_js_dir, 'chart.umd.min.js') chartjs_urls = [ 'https://cdn.jsdelivr.net/npm/chart.js@latest/dist/chart.umd.min.js', # Try latest first 'https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js' # Fallback to known working version ] chartjs_downloaded = False for chartjs_url in chartjs_urls: command = f'wget -q --timeout=30 {chartjs_url} -O {chartjs_path}' result = subprocess.call(shlex.split(command)) if result == 0 and os.path.exists(chartjs_path) and os.path.getsize(chartjs_path) > 100000: # At least 100KB os.chmod(chartjs_path, 0o644) version_info = "latest" if "latest" in chartjs_url else "4.4.1" Upgrade.stdOut(f"Downloaded chart.umd.min.js ({version_info})", 0) chartjs_downloaded = True # Create copy for chart.js compatibility (some code may expect chart.js name) chartjs_compat_path = os.path.join(custom_js_dir, 'chart.js') if not os.path.exists(chartjs_compat_path): shutil.copy2(chartjs_path, chartjs_compat_path) break if not chartjs_downloaded: Upgrade.stdOut("Warning: Failed to download chart.umd.min.js, continuing anyway", 0) except BaseException as msg: ErrorSanitizer.log_error_securely(msg, 'downloadCDNLibraries') Upgrade.stdOut(f"Warning: Error downloading CDN libraries: {str(msg)}, continuing anyway", 0) @staticmethod def staticContent(): command = "rm -rf /usr/local/CyberCP/public/static" Upgrade.executioner(command, 'Remove old static content', 0) ## if not os.path.exists("/usr/local/CyberCP/public"): os.mkdir("/usr/local/CyberCP/public") # Download CDN libraries before collectstatic runs Upgrade.downloadCDNLibraries() cwd = os.getcwd() os.chdir('/usr/local/CyberCP') py = Upgrade._python_for_manage() command = py + ' manage.py collectstatic --noinput --clear' Upgrade.executioner(command, 'Remove old static content', 0) os.chdir(cwd) shutil.move("/usr/local/CyberCP/static", "/usr/local/CyberCP/public/") @staticmethod def upgradeVersion(): try: import django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") django.setup() from baseTemplate.models import version vers = version.objects.get(pk=1) vers.currentVersion = VERSION vers.build = str(BUILD) vers.save() except: pass @staticmethod def setupConnection(db=None): try: passFile = "/etc/cyberpanel/mysqlPassword" f = open(passFile) data = f.read() password = data.split('\n', 1)[0] if db == None: conn = mysql.connect(user='root', passwd=password) else: try: conn = mysql.connect(db=db, user='root', passwd=password) except: try: conn = mysql.connect(host='127.0.0.1', port=3307, db=db, user='root', passwd=password) except: dbUser = settings.DATABASES['default']['USER'] password = settings.DATABASES['default']['PASSWORD'] host = settings.DATABASES['default']['HOST'] port = settings.DATABASES['default']['PORT'] if port == '': conn = mysql.connect(host=host, port=3306, db=db, user=dbUser, passwd=password) else: conn = mysql.connect(host=host, port=int(port), db=db, user=dbUser, passwd=password) cursor = conn.cursor() return conn, cursor except Exception as e: ErrorSanitizer.log_error_securely(e, 'database_connection') Upgrade.stdOut("Failed to establish database connection") return 0, 0 @staticmethod def applyLoginSystemMigrations(): try: connection, cursor = Upgrade.setupConnection('cyberpanel') try: cursor.execute( 'CREATE TABLE `baseTemplate_cyberpanelcosmetic` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `MainDashboardCSS` longtext NOT NULL)') except: pass # AI Scanner Tables try: cursor.execute(''' CREATE TABLE `ai_scanner_settings` ( `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `admin_id` integer NOT NULL UNIQUE, `api_key` varchar(255) DEFAULT NULL, `balance` decimal(10,4) NOT NULL DEFAULT 0.0000, `is_payment_configured` bool NOT NULL DEFAULT 0, `created_at` datetime(6) NOT NULL, `updated_at` datetime(6) NOT NULL, KEY `ai_scanner_settings_admin_id_idx` (`admin_id`), CONSTRAINT `ai_scanner_settings_admin_id_fk` FOREIGN KEY (`admin_id`) REFERENCES `loginSystem_administrator` (`id`) ON DELETE CASCADE ) ''') except: pass try: cursor.execute(''' CREATE TABLE `ai_scanner_history` ( `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `admin_id` integer NOT NULL, `scan_id` varchar(100) NOT NULL UNIQUE, `domain` varchar(255) NOT NULL, `scan_type` varchar(20) NOT NULL DEFAULT 'full', `status` varchar(20) NOT NULL DEFAULT 'pending', `cost_usd` decimal(10,6) DEFAULT NULL, `files_scanned` integer NOT NULL DEFAULT 0, `issues_found` integer NOT NULL DEFAULT 0, `findings_json` longtext DEFAULT NULL, `summary_json` longtext DEFAULT NULL, `error_message` longtext DEFAULT NULL, `started_at` datetime(6) NOT NULL, `completed_at` datetime(6) DEFAULT NULL, KEY `ai_scanner_history_admin_id_idx` (`admin_id`), KEY `ai_scanner_history_scan_id_idx` (`scan_id`), KEY `ai_scanner_history_started_at_idx` (`started_at`), CONSTRAINT `ai_scanner_history_admin_id_fk` FOREIGN KEY (`admin_id`) REFERENCES `loginSystem_administrator` (`id`) ON DELETE CASCADE ) ''') except: pass try: cursor.execute(''' CREATE TABLE `ai_scanner_file_tokens` ( `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `token` varchar(100) NOT NULL UNIQUE, `scan_history_id` integer NOT NULL, `domain` varchar(255) NOT NULL, `wp_path` varchar(500) NOT NULL, `expires_at` datetime(6) NOT NULL, `created_at` datetime(6) NOT NULL, `is_active` bool NOT NULL DEFAULT 1, KEY `ai_scanner_file_tokens_scan_history_id_idx` (`scan_history_id`), KEY `ai_scanner_file_tokens_token_idx` (`token`), CONSTRAINT `ai_scanner_file_tokens_scan_history_id_fk` FOREIGN KEY (`scan_history_id`) REFERENCES `ai_scanner_history` (`id`) ON DELETE CASCADE ) ''') except: pass try: cursor.execute(''' CREATE TABLE `ai_scanner_status_updates` ( `scan_id` varchar(100) NOT NULL PRIMARY KEY, `phase` varchar(50) NOT NULL, `progress` integer NOT NULL DEFAULT 0, `current_file` longtext DEFAULT NULL, `files_discovered` integer NOT NULL DEFAULT 0, `files_scanned` integer NOT NULL DEFAULT 0, `files_remaining` integer NOT NULL DEFAULT 0, `threats_found` integer NOT NULL DEFAULT 0, `critical_threats` integer NOT NULL DEFAULT 0, `high_threats` integer NOT NULL DEFAULT 0, `activity_description` longtext DEFAULT NULL, `last_updated` datetime(6) NOT NULL, `created_at` datetime(6) NOT NULL, KEY `ai_scanner_status_updates_scan_id_last_updated_idx` (`scan_id`, `last_updated` DESC) ) ''') except: pass # AI Scanner Scheduled Scans Tables try: cursor.execute(''' CREATE TABLE `ai_scanner_scheduled_scans` ( `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `admin_id` integer NOT NULL, `name` varchar(200) NOT NULL, `domains` longtext NOT NULL, `frequency` varchar(20) NOT NULL DEFAULT 'weekly', `scan_type` varchar(20) NOT NULL DEFAULT 'full', `time_of_day` time NOT NULL, `day_of_week` integer DEFAULT NULL, `day_of_month` integer DEFAULT NULL, `status` varchar(20) NOT NULL DEFAULT 'active', `last_run` datetime(6) DEFAULT NULL, `next_run` datetime(6) DEFAULT NULL, `created_at` datetime(6) NOT NULL, `updated_at` datetime(6) NOT NULL, `email_notifications` bool NOT NULL DEFAULT 1, `notification_emails` longtext NOT NULL DEFAULT '', `notify_on_threats` bool NOT NULL DEFAULT 1, `notify_on_completion` bool NOT NULL DEFAULT 0, `notify_on_failure` bool NOT NULL DEFAULT 1, KEY `ai_scanner_scheduled_scans_admin_id_idx` (`admin_id`), KEY `ai_scanner_scheduled_scans_status_next_run_idx` (`status`, `next_run`), CONSTRAINT `ai_scanner_scheduled_scans_admin_id_fk` FOREIGN KEY (`admin_id`) REFERENCES `loginSystem_administrator` (`id`) ON DELETE CASCADE ) ''') except: pass try: cursor.execute(''' CREATE TABLE `ai_scanner_scheduled_executions` ( `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `scheduled_scan_id` integer NOT NULL, `execution_time` datetime(6) NOT NULL, `status` varchar(20) NOT NULL DEFAULT 'pending', `domains_scanned` longtext NOT NULL DEFAULT '', `total_scans` integer NOT NULL DEFAULT 0, `successful_scans` integer NOT NULL DEFAULT 0, `failed_scans` integer NOT NULL DEFAULT 0, `total_cost` decimal(10,6) NOT NULL DEFAULT 0.000000, `scan_ids` longtext NOT NULL DEFAULT '', `error_message` longtext DEFAULT NULL, `started_at` datetime(6) DEFAULT NULL, `completed_at` datetime(6) DEFAULT NULL, KEY `ai_scanner_scheduled_executions_scheduled_scan_id_idx` (`scheduled_scan_id`), KEY `ai_scanner_scheduled_executions_execution_time_idx` (`execution_time` DESC), CONSTRAINT `ai_scanner_scheduled_executions_scheduled_scan_id_fk` FOREIGN KEY (`scheduled_scan_id`) REFERENCES `ai_scanner_scheduled_scans` (`id`) ON DELETE CASCADE ) ''') except: pass try: cursor.execute( 'CREATE TABLE `loginSystem_acl` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(50) NOT NULL UNIQUE, `adminStatus` integer NOT NULL DEFAULT 0, `versionManagement` integer NOT NULL DEFAULT 0, `createNewUser` integer NOT NULL DEFAULT 0, `deleteUser` integer NOT NULL DEFAULT 0, `resellerCenter` integer NOT NULL DEFAULT 0, `changeUserACL` integer NOT NULL DEFAULT 0, `createWebsite` integer NOT NULL DEFAULT 0, `modifyWebsite` integer NOT NULL DEFAULT 0, `suspendWebsite` integer NOT NULL DEFAULT 0, `deleteWebsite` integer NOT NULL DEFAULT 0, `createPackage` integer NOT NULL DEFAULT 0, `deletePackage` integer NOT NULL DEFAULT 0, `modifyPackage` integer NOT NULL DEFAULT 0, `createDatabase` integer NOT NULL DEFAULT 0, `deleteDatabase` integer NOT NULL DEFAULT 0, `listDatabases` integer NOT NULL DEFAULT 0, `createNameServer` integer NOT NULL DEFAULT 0, `createDNSZone` integer NOT NULL DEFAULT 0, `deleteZone` integer NOT NULL DEFAULT 0, `addDeleteRecords` integer NOT NULL DEFAULT 0, `createEmail` integer NOT NULL DEFAULT 0, `deleteEmail` integer NOT NULL DEFAULT 0, `emailForwarding` integer NOT NULL DEFAULT 0, `changeEmailPassword` integer NOT NULL DEFAULT 0, `dkimManager` integer NOT NULL DEFAULT 0, `createFTPAccount` integer NOT NULL DEFAULT 0, `deleteFTPAccount` integer NOT NULL DEFAULT 0, `listFTPAccounts` integer NOT NULL DEFAULT 0, `createBackup` integer NOT NULL DEFAULT 0, `restoreBackup` integer NOT NULL DEFAULT 0, `addDeleteDestinations` integer NOT NULL DEFAULT 0, `scheduleBackups` integer NOT NULL DEFAULT 0, `remoteBackups` integer NOT NULL DEFAULT 0, `manageSSL` integer NOT NULL DEFAULT 0, `hostnameSSL` integer NOT NULL DEFAULT 0, `mailServerSSL` integer NOT NULL DEFAULT 0)') except: pass try: cursor.execute('ALTER TABLE loginSystem_administrator ADD token varchar(500)') except: pass try: cursor.execute("ALTER TABLE loginSystem_administrator ADD secretKey varchar(50) DEFAULT 'None'") except: pass try: cursor.execute('alter table databases_databases drop index dbUser;') except: pass try: cursor.execute("ALTER TABLE loginSystem_administrator ADD state varchar(15) DEFAULT 'ACTIVE'") except: pass try: cursor.execute('ALTER TABLE loginSystem_administrator ADD securityLevel integer DEFAULT 1') except: pass try: cursor.execute('ALTER TABLE loginSystem_administrator ADD defaultSite integer DEFAULT 0') except: pass try: cursor.execute('ALTER TABLE loginSystem_administrator ADD twoFA integer DEFAULT 0') except: pass try: cursor.execute('ALTER TABLE loginSystem_administrator ADD api integer') except: pass try: cursor.execute('ALTER TABLE loginSystem_administrator ADD acl_id integer') except: pass try: cursor.execute( 'ALTER TABLE loginSystem_administrator ADD FOREIGN KEY (acl_id) REFERENCES loginSystem_acl(id)') except: pass try: cursor.execute("insert into loginSystem_acl (id, name, adminStatus) values (1,'admin',1)") except: pass try: cursor.execute( "insert into loginSystem_acl (id, name, adminStatus, createNewUser, deleteUser, createWebsite, resellerCenter, modifyWebsite, suspendWebsite, deleteWebsite, createPackage, deletePackage, modifyPackage, createNameServer, restoreBackup) values (2,'reseller',0,1,1,1,1,1,1,1,1,1,1,1,1)") except: pass try: cursor.execute( "insert into loginSystem_acl (id, name, createDatabase, deleteDatabase, listDatabases, createDNSZone, deleteZone, addDeleteRecords, createEmail, deleteEmail, emailForwarding, changeEmailPassword, dkimManager, createFTPAccount, deleteFTPAccount, listFTPAccounts, createBackup, manageSSL) values (3,'user', 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)") except: pass try: cursor.execute("UPDATE loginSystem_administrator SET acl_id = 1 where userName = 'admin'") except: pass try: cursor.execute('ALTER TABLE loginSystem_acl ADD config longtext') except: pass try: cursor.execute("UPDATE loginSystem_acl SET config = '%s' where name = 'admin'" % (Upgrade.AdminACL)) except Exception as e: ErrorSanitizer.log_error_securely(e, 'applyLoginSystemMigrations') try: import sleep except: from time import sleep from time import sleep sleep(10) try: cursor.execute( "UPDATE loginSystem_acl SET config = '%s' where name = 'reseller'" % (Upgrade.ResellerACL)) except: pass try: cursor.execute("UPDATE loginSystem_acl SET config = '%s' where name = 'user'" % (Upgrade.UserACL)) except: pass try: cursor.execute("alter table loginSystem_administrator drop initUserAccountsLimit") except: pass try: cursor.execute( "CREATE TABLE `websiteFunctions_aliasdomains` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `aliasDomain` varchar(75) NOT NULL)") except: pass try: cursor.execute("ALTER TABLE `websiteFunctions_aliasdomains` ADD COLUMN `master_id` integer NOT NULL") except: pass try: cursor.execute( "ALTER TABLE `websiteFunctions_aliasdomains` ADD CONSTRAINT `websiteFunctions_ali_master_id_726c433d_fk_websiteFu` FOREIGN KEY (`master_id`) REFERENCES `websiteFunctions_websites` (`id`)") except: pass try: cursor.execute('ALTER TABLE websiteFunctions_websites ADD config longtext') except: pass try: cursor.execute("ALTER TABLE websiteFunctions_websites MODIFY externalApp varchar(30)") except: pass try: cursor.execute("ALTER TABLE emailMarketing_smtphosts MODIFY userName varchar(200)") except: pass try: cursor.execute("ALTER TABLE emailMarketing_smtphosts MODIFY password varchar(200)") except: pass try: cursor.execute("ALTER TABLE websiteFunctions_backups MODIFY fileName varchar(200)") except: pass try: cursor.execute("ALTER TABLE loginSystem_acl ADD COLUMN listUsers INT DEFAULT 0;") except: pass try: cursor.execute("ALTER TABLE loginSystem_acl ADD COLUMN listEmails INT DEFAULT 1;") except: pass try: cursor.execute("ALTER TABLE loginSystem_acl ADD COLUMN listPackages INT DEFAULT 0;") except: pass query = """CREATE TABLE `websiteFunctions_normalbackupdests` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(25) NOT NULL, `config` longtext NOT NULL, PRIMARY KEY (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `cloudAPI_wpdeployments` ( `id` int(11) NOT NULL AUTO_INCREMENT, `config` longtext NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `cloudAPI_wpdeploymen_owner_id_506ddf01_fk_websiteFu` (`owner_id`), CONSTRAINT `cloudAPI_wpdeploymen_owner_id_506ddf01_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_websites` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_normalbackupjobs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(25) NOT NULL, `config` longtext NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `websiteFunctions_nor_owner_id_3a7a13db_fk_websiteFu` (`owner_id`), CONSTRAINT `websiteFunctions_nor_owner_id_3a7a13db_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_normalbackupdests` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_normalbackupsites` ( `id` int(11) NOT NULL AUTO_INCREMENT, `domain_id` int(11) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `websiteFunctions_nor_domain_id_c03362bc_fk_websiteFu` (`domain_id`), KEY `websiteFunctions_nor_owner_id_c6ece6cc_fk_websiteFu` (`owner_id`), CONSTRAINT `websiteFunctions_nor_domain_id_c03362bc_fk_websiteFu` FOREIGN KEY (`domain_id`) REFERENCES `websiteFunctions_websites` (`id`), CONSTRAINT `websiteFunctions_nor_owner_id_c6ece6cc_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_normalbackupjobs` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_normalbackupjoblogs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `status` int(11) NOT NULL, `message` longtext NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `websiteFunctions_nor_owner_id_69403e73_fk_websiteFu` (`owner_id`), CONSTRAINT `websiteFunctions_nor_owner_id_69403e73_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_normalbackupjobs` (`id`) )""" try: cursor.execute(query) except: pass try: cursor.execute('ALTER TABLE e_users ADD DiskUsage varchar(200)') except: pass try: cursor.execute( 'CREATE TABLE `websiteFunctions_wpplugins` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `Name` varchar(255) NOT NULL, `config` longtext NOT NULL, `owner_id` integer NOT NULL)') except: pass try: cursor.execute( 'ALTER TABLE `websiteFunctions_wpplugins` ADD CONSTRAINT `websiteFunctions_wpp_owner_id_493a02c7_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`)') except: pass try: cursor.execute( 'CREATE TABLE `websiteFunctions_wpsites` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `title` varchar(255) NOT NULL, `path` varchar(255) NOT NULL, `FinalURL` varchar(255) NOT NULL, `AutoUpdates` varchar(100) NOT NULL, `PluginUpdates` varchar(15) NOT NULL, `ThemeUpdates` varchar(15) NOT NULL, `date` datetime(6) NOT NULL, `WPLockState` integer NOT NULL, `owner_id` integer NOT NULL)') except: pass try: cursor.execute( 'ALTER TABLE `websiteFunctions_wpsites` ADD CONSTRAINT `websiteFunctions_wps_owner_id_6d67df2a_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_websites` (`id`)') except: pass try: cursor.execute( 'CREATE TABLE `websiteFunctions_wpstaging` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `owner_id` integer NOT NULL, `wpsite_id` integer NOT NULL)') except: pass try: cursor.execute( 'ALTER TABLE `websiteFunctions_wpstaging` ADD CONSTRAINT `websiteFunctions_wps_owner_id_543d8aec_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_wpsites` (`id`);') except: pass try: cursor.execute( 'ALTER TABLE `websiteFunctions_wpstaging` ADD CONSTRAINT `websiteFunctions_wps_wpsite_id_82843593_fk_websiteFu` FOREIGN KEY (`wpsite_id`) REFERENCES `websiteFunctions_wpsites` (`id`)') except: pass try: cursor.execute( "CREATE TABLE `websiteFunctions_wpsitesbackup` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `WPSiteID` integer NOT NULL, `WebsiteID` integer NOT NULL, `config` longtext NOT NULL, `owner_id` integer NOT NULL); ") except: pass try: cursor.execute( "ALTER TABLE `websiteFunctions_wpsitesbackup` ADD CONSTRAINT `websiteFunctions_wps_owner_id_8a8dd0c5_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`); ") except: pass query = """CREATE TABLE `websiteFunctions_remotebackupconfig` ( `id` int(11) NOT NULL AUTO_INCREMENT, `configtype` varchar(255) NOT NULL, `config` longtext NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_remotebackupschedule` ( `id` int(11) NOT NULL AUTO_INCREMENT, `Name` varchar(255) NOT NULL, `timeintervel` varchar(200) NOT NULL, `fileretention` varchar(200) NOT NULL, `lastrun` varchar(200) NOT NULL, `config` longtext NOT NULL, `RemoteBackupConfig_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `websiteFunctions_rem_RemoteBackupConfig_i_224c46fb_fk_websiteFu` (`RemoteBackupConfig_id`), CONSTRAINT `websiteFunctions_rem_RemoteBackupConfig_i_224c46fb_fk_websiteFu` FOREIGN KEY (`RemoteBackupConfig_id`) REFERENCES `websiteFunctions_remotebackupconfig` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_remotebackupsites` ( `id` int(11) NOT NULL AUTO_INCREMENT, `WPsites` int(11) DEFAULT NULL, `database` int(11) DEFAULT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `websiteFunctions_rem_owner_id_d6c4475a_fk_websiteFu` (`owner_id`), CONSTRAINT `websiteFunctions_rem_owner_id_d6c4475a_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_remotebackupschedule` (`id`) )""" try: cursor.execute(query) except: pass query = """ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `fileName` varchar(255) NOT NULL, `status` integer NOT NULL, `timeStamp` varchar(255) NOT NULL, `BasePath` longtext NOT NULL, `website_id` integer NOT NULL); """ try: cursor.execute(query) except: pass query = "ALTER TABLE `websiteFunctions_backupsv2` ADD CONSTRAINT `websiteFunctions_bac_website_id_3a777e68_fk_websiteFu` FOREIGN KEY (`website_id`) REFERENCES `websiteFunctions_websites` (`id`);" try: cursor.execute(query) except: pass query = "ALTER TABLE `websiteFunctions_backupslogsv2` ADD CONSTRAINT `websiteFunctions_bac_owner_id_9e884ff9_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_backupsv2` (`id`);" try: cursor.execute(query) except: pass query = "CREATE TABLE `websiteFunctions_backupslogsv2` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `timeStamp` varchar(255) NOT NULL, `message` longtext NOT NULL, `owner_id` integer NOT NULL);" try: cursor.execute(query) except: pass query = "ALTER TABLE `websiteFunctions_backupslogsv2` ADD CONSTRAINT `websiteFunctions_bac_owner_id_9e884ff9_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_backupsv2` (`id`);" try: cursor.execute(query) except: pass try: cursor.execute("ALTER TABLE websiteFunctions_websites ADD COLUMN BackupLock INT DEFAULT 0;") except: pass ### update ftp issue for ubuntu 22 try: cursor.execute( 'ALTER TABLE `users` CHANGE `Password` `Password` VARCHAR(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL; ') except: pass query = "CREATE TABLE `IncBackups_oneclickbackups` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `planName` varchar(100) NOT NULL, `months` varchar(100) NOT NULL, `price` varchar(100) NOT NULL, `customer` varchar(255) NOT NULL, `subscription` varchar(255) NOT NULL UNIQUE, `sftpUser` varchar(100) NOT NULL, `config` longtext NOT NULL, `date` datetime(6) NOT NULL, `state` integer NOT NULL, `owner_id` integer NOT NULL);" try: cursor.execute(query) except: pass query = 'ALTER TABLE `IncBackups_oneclickbackups` ADD CONSTRAINT `IncBackups_oneclickb_owner_id_7b4250a4_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`);' try: cursor.execute(query) except: pass if Upgrade.FindOperatingSytem() == Ubuntu22 or Upgrade.FindOperatingSytem() == Ubuntu24 or Upgrade.FindOperatingSytem() == Debian11 or Upgrade.FindOperatingSytem() == Debian12 or Upgrade.FindOperatingSytem() == Debian13: ### If ftp not installed then upgrade will fail so this command should not do exit command = "sed -i 's/MYSQLCrypt md5/MYSQLCrypt crypt/g' /etc/pure-ftpd/db/mysql.conf" Upgrade.executioner(command, command, 0) command = "systemctl restart pure-ftpd-mysql.service" Upgrade.executioner(command, command, 0) try: clAPVersion = Upgrade.FetchCloudLinuxAlmaVersionVersion() if isinstance(clAPVersion, str) and '-' in clAPVersion: type = clAPVersion.split('-')[0] version = int(clAPVersion.split('-')[1]) if type == 'al' and version >= 90: command = "sed -i 's/MYSQLCrypt md5/MYSQLCrypt crypt/g' /etc/pure-ftpd/pureftpd-mysql.conf" Upgrade.executioner(command, command, 0) except: pass try: connection.close() except: pass except OSError as msg: Upgrade.stdOut(str(msg) + " [applyLoginSystemMigrations]") @staticmethod def homeDirectoryMigrations(): """Create home_directories and user_home_mappings tables if missing (Modify Website home directory feature).""" try: connection, cursor = Upgrade.setupConnection('cyberpanel') try: cursor.execute(""" CREATE TABLE IF NOT EXISTS `home_directories` ( `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(50) NOT NULL UNIQUE, `path` varchar(255) NOT NULL UNIQUE, `is_active` tinyint(1) NOT NULL DEFAULT 1, `is_default` tinyint(1) NOT NULL DEFAULT 0, `max_users` integer NOT NULL DEFAULT 0, `description` longtext, `created_at` datetime(6) NOT NULL, `updated_at` datetime(6) NOT NULL ) """) except Exception: pass try: cursor.execute(""" CREATE TABLE IF NOT EXISTS `user_home_mappings` ( `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `user_id` integer NOT NULL UNIQUE, `home_directory_id` integer NOT NULL, `created_at` datetime(6) NOT NULL, `updated_at` datetime(6) NOT NULL, CONSTRAINT `user_home_mappings_user_id_fk` FOREIGN KEY (`user_id`) REFERENCES `loginSystem_administrator` (`id`) ON DELETE CASCADE, CONSTRAINT `user_home_mappings_home_directory_id_fk` FOREIGN KEY (`home_directory_id`) REFERENCES `home_directories` (`id`) ON DELETE CASCADE ) """) except Exception: pass try: connection.close() except Exception: pass except Exception: pass @staticmethod def s3BackupMigrations(): try: connection, cursor = Upgrade.setupConnection('cyberpanel') query = """CREATE TABLE `s3Backups_backupplan` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `bucket` varchar(50) NOT NULL, `freq` varchar(50) NOT NULL, `retention` int(11) NOT NULL, `type` varchar(5) NOT NULL, `lastRun` varchar(50) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `s3Backups_backupplan_owner_id_7d058ced_fk_loginSyst` (`owner_id`), CONSTRAINT `s3Backups_backupplan_owner_id_7d058ced_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) )""" try: cursor.execute(query) except: pass try: cursor.execute('ALTER TABLE s3Backups_backupplan ADD config longtext') except: pass query = """CREATE TABLE `s3Backups_websitesinplan` ( `id` int(11) NOT NULL AUTO_INCREMENT, `domain` varchar(100) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `s3Backups_websitesin_owner_id_0e9a4fe3_fk_s3Backups` (`owner_id`), CONSTRAINT `s3Backups_websitesin_owner_id_0e9a4fe3_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplan` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `s3Backups_backuplogs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `timeStamp` varchar(200) NOT NULL, `level` varchar(5) NOT NULL, `msg` varchar(500) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `s3Backups_backuplogs_owner_id_7b4653af_fk_s3Backups` (`owner_id`), CONSTRAINT `s3Backups_backuplogs_owner_id_7b4653af_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplan` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `s3Backups_backupplando` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `bucket` varchar(50) NOT NULL, `freq` varchar(50) NOT NULL, `retention` int(11) NOT NULL, `type` varchar(5) NOT NULL, `region` varchar(5) NOT NULL, `lastRun` varchar(50) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `s3Backups_backupplan_owner_id_1a3ec86d_fk_loginSyst` (`owner_id`), CONSTRAINT `s3Backups_backupplan_owner_id_1a3ec86d_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `s3Backups_websitesinplando` ( `id` int(11) NOT NULL AUTO_INCREMENT, `domain` varchar(100) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `s3Backups_websitesin_owner_id_cef3ea04_fk_s3Backups` (`owner_id`), CONSTRAINT `s3Backups_websitesin_owner_id_cef3ea04_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplando` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `s3Backups_backuplogsdo` ( `id` int(11) NOT NULL AUTO_INCREMENT, `timeStamp` varchar(200) NOT NULL, `level` varchar(5) NOT NULL, `msg` varchar(500) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `s3Backups_backuplogs_owner_id_c7cb5872_fk_s3Backups` (`owner_id`), CONSTRAINT `s3Backups_backuplogs_owner_id_c7cb5872_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplando` (`id`) )""" try: cursor.execute(query) except: pass ## query = """CREATE TABLE `s3Backups_minionodes` ( `id` int(11) NOT NULL AUTO_INCREMENT, `endPointURL` varchar(200) NOT NULL, `accessKey` varchar(200) NOT NULL, `secretKey` varchar(200) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `endPointURL` (`endPointURL`), UNIQUE KEY `accessKey` (`accessKey`), KEY `s3Backups_minionodes_owner_id_e50993d9_fk_loginSyst` (`owner_id`), CONSTRAINT `s3Backups_minionodes_owner_id_e50993d9_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `s3Backups_backupplanminio` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `freq` varchar(50) NOT NULL, `retention` int(11) NOT NULL, `lastRun` varchar(50) NOT NULL, `minioNode_id` int(11) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `s3Backups_backupplan_minioNode_id_a4eaf917_fk_s3Backups` (`minioNode_id`), KEY `s3Backups_backupplan_owner_id_d6830e67_fk_loginSyst` (`owner_id`), CONSTRAINT `s3Backups_backupplan_minioNode_id_a4eaf917_fk_s3Backups` FOREIGN KEY (`minioNode_id`) REFERENCES `s3Backups_minionodes` (`id`), CONSTRAINT `s3Backups_backupplan_owner_id_d6830e67_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `s3Backups_websitesinplanminio` ( `id` int(11) NOT NULL AUTO_INCREMENT, `domain` varchar(100) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `s3Backups_websitesin_owner_id_224ce049_fk_s3Backups` (`owner_id`), CONSTRAINT `s3Backups_websitesin_owner_id_224ce049_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplanminio` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `s3Backups_backuplogsminio` ( `id` int(11) NOT NULL AUTO_INCREMENT, `timeStamp` varchar(200) NOT NULL, `level` varchar(5) NOT NULL, `msg` varchar(500) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `s3Backups_backuplogs_owner_id_f19e1736_fk_s3Backups` (`owner_id`), CONSTRAINT `s3Backups_backuplogs_owner_id_f19e1736_fk_s3Backups` FOREIGN KEY (`owner_id`) REFERENCES `s3Backups_backupplanminio` (`id`) )""" try: cursor.execute(query) except: pass try: connection.close() except: pass except OSError as msg: Upgrade.stdOut(str(msg) + " [applyLoginSystemMigrations]") @staticmethod def mailServerMigrations(): try: connection, cursor = Upgrade.setupConnection('cyberpanel') try: cursor.execute( 'ALTER TABLE `e_domains` ADD COLUMN `childOwner_id` integer') except: pass try: cursor.execute( 'ALTER TABLE e_users ADD mail varchar(200)') except: pass try: cursor.execute( 'ALTER TABLE e_users MODIFY password varchar(200)') except: pass try: cursor.execute( 'ALTER TABLE e_forwardings DROP PRIMARY KEY;ALTER TABLE e_forwardings ADD id INT AUTO_INCREMENT PRIMARY KEY') except: pass query = """CREATE TABLE `emailPremium_domainlimits` ( `id` int(11) NOT NULL AUTO_INCREMENT, `limitStatus` int(11) NOT NULL, `monthlyLimit` int(11) NOT NULL, `monthlyUsed` int(11) NOT NULL, `domain_id` varchar(50) NOT NULL, PRIMARY KEY (`id`), KEY `emailPremium_domainlimits_domain_id_303ab297_fk_e_domains_domain` (`domain_id`), CONSTRAINT `emailPremium_domainlimits_domain_id_303ab297_fk_e_domains_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `emailPremium_emaillimits` ( `id` int(11) NOT NULL AUTO_INCREMENT, `limitStatus` int(11) NOT NULL, `monthlyLimits` int(11) NOT NULL, `monthlyUsed` int(11) NOT NULL, `hourlyLimit` int(11) NOT NULL, `hourlyUsed` int(11) NOT NULL, `emailLogs` int(11) NOT NULL, `email_id` varchar(80) NOT NULL, PRIMARY KEY (`id`), KEY `emailPremium_emaillimits_email_id_1c111df5_fk_e_users_email` (`email_id`), CONSTRAINT `emailPremium_emaillimits_email_id_1c111df5_fk_e_users_email` FOREIGN KEY (`email_id`) REFERENCES `e_users` (`email`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `emailPremium_emaillogs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `destination` varchar(200) NOT NULL, `timeStamp` varchar(200) NOT NULL, `email_id` varchar(80) NOT NULL, PRIMARY KEY (`id`), KEY `emailPremium_emaillogs_email_id_9ef49552_fk_e_users_email` (`email_id`), CONSTRAINT `emailPremium_emaillogs_email_id_9ef49552_fk_e_users_email` FOREIGN KEY (`email_id`) REFERENCES `e_users` (`email`) )""" try: cursor.execute(query) except: pass # Email Filtering Tables - Catch-All, Plus-Addressing, Pattern Forwarding query = """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`), CONSTRAINT `fk_catchall_domain` FOREIGN KEY (`domain_id`) REFERENCES `e_domains` (`domain`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4""" try: cursor.execute(query) except: pass try: connection.close() except: pass except: pass @staticmethod def emailMarketingMigrationsa(): try: connection, cursor = Upgrade.setupConnection('cyberpanel') query = """CREATE TABLE `emailMarketing_emailmarketing` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userName` varchar(50) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `userName` (`userName`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `emailMarketing_emaillists` ( `id` int(11) NOT NULL AUTO_INCREMENT, `listName` varchar(50) NOT NULL, `dateCreated` varchar(200) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `listName` (`listName`), KEY `emailMarketing_email_owner_id_bf1b4530_fk_websiteFu` (`owner_id`), CONSTRAINT `emailMarketing_email_owner_id_bf1b4530_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_websites` (`id`) )""" try: cursor.execute(query) except: pass query = 'ALTER TABLE emailMarketing_emaillists ADD COLUMN verified INT DEFAULT 0' try: cursor.execute(query) except: pass query = 'ALTER TABLE emailMarketing_emaillists ADD COLUMN notVerified INT DEFAULT 0' try: cursor.execute(query) except: pass query = """CREATE TABLE `emailMarketing_emailsinlist` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(50) NOT NULL, `firstName` varchar(20) NOT NULL, `lastName` varchar(20) NOT NULL, `verificationStatus` varchar(100) NOT NULL, `dateCreated` varchar(200) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `emailMarketing_email_owner_id_c5c27005_fk_emailMark` (`owner_id`), CONSTRAINT `emailMarketing_email_owner_id_c5c27005_fk_emailMark` FOREIGN KEY (`owner_id`) REFERENCES `emailMarketing_emaillists` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `emailMarketing_smtphosts` ( `id` int(11) NOT NULL AUTO_INCREMENT, `host` varchar(150) NOT NULL, `port` varchar(10) NOT NULL, `userName` varchar(50) NOT NULL, `password` varchar(50) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `host` (`host`), KEY `emailMarketing_smtph_owner_id_8b2d4ac7_fk_loginSyst` (`owner_id`), CONSTRAINT `emailMarketing_smtph_owner_id_8b2d4ac7_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `emailMarketing_emailtemplate` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `subject` varchar(1000) NOT NULL, `fromName` varchar(100) NOT NULL, `fromEmail` varchar(150) NOT NULL, `replyTo` varchar(150) NOT NULL, `emailMessage` varchar(30000) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `emailMarketing_email_owner_id_d27e1d00_fk_loginSyst` (`owner_id`), CONSTRAINT `emailMarketing_email_owner_id_d27e1d00_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `emailMarketing_emailjobs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `date` varchar(200) NOT NULL, `host` varchar(1000) NOT NULL, `totalEmails` int(11) NOT NULL, `sent` int(11) NOT NULL, `failed` int(11) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `emailMarketing_email_owner_id_73ee4827_fk_emailMark` (`owner_id`), CONSTRAINT `emailMarketing_email_owner_id_73ee4827_fk_emailMark` FOREIGN KEY (`owner_id`) REFERENCES `emailMarketing_emailtemplate` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `mailServer_pipeprograms` ( `id` int(11) NOT NULL AUTO_INCREMENT, `source` varchar(80) NOT NULL, `destination` longtext NOT NULL, PRIMARY KEY (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `emailMarketing_validationlog` ( `id` int(11) NOT NULL AUTO_INCREMENT, `status` int(11) NOT NULL, `message` longtext NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `emailMarketing_valid_owner_id_240ad36e_fk_emailMark` (`owner_id`), CONSTRAINT `emailMarketing_valid_owner_id_240ad36e_fk_emailMark` FOREIGN KEY (`owner_id`) REFERENCES `emailMarketing_emaillists` (`id`) )""" try: cursor.execute(query) except: pass try: connection.close() except: pass except: pass @staticmethod def dockerMigrations(): try: connection, cursor = Upgrade.setupConnection('cyberpanel') query = """CREATE TABLE `dockerManager_containers` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `cid` varchar(64) NOT NULL, `image` varchar(50) NOT NULL, `tag` varchar(50) NOT NULL, `memory` int(11) NOT NULL, `ports` longtext NOT NULL, `env` longtext NOT NULL, `startOnReboot` int(11) NOT NULL, `admin_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `dockerManager_contai_admin_id_58fb62b7_fk_loginSyst` (`admin_id`), CONSTRAINT `dockerManager_contai_admin_id_58fb62b7_fk_loginSyst` FOREIGN KEY (`admin_id`) REFERENCES `loginSystem_administrator` (`id`) )""" try: cursor.execute(query) except: pass try: cursor.execute('ALTER TABLE loginSystem_administrator ADD config longtext') except: pass try: cursor.execute('ALTER TABLE loginSystem_acl ADD config longtext') except: pass try: cursor.execute('ALTER TABLE dockerManager_containers ADD volumes longtext') except: pass try: cursor.execute('ALTER TABLE dockerManager_containers MODIFY COLUMN name VARCHAR(150);') except: pass # Add new fields for network configuration and extra options try: cursor.execute('ALTER TABLE dockerManager_containers ADD network VARCHAR(100) DEFAULT "bridge"') except: pass try: cursor.execute('ALTER TABLE dockerManager_containers ADD network_mode VARCHAR(50) DEFAULT "bridge"') except: pass try: cursor.execute('ALTER TABLE dockerManager_containers ADD extra_options LONGTEXT DEFAULT "{}"') except: pass try: connection.close() except: pass except: pass @staticmethod def containerMigrations(): try: connection, cursor = Upgrade.setupConnection('cyberpanel') query = """CREATE TABLE `containerization_containerlimits` ( `id` int(11) NOT NULL AUTO_INCREMENT, `cpuPers` varchar(10) NOT NULL, `IO` varchar(10) NOT NULL, `IOPS` varchar(10) NOT NULL, `memory` varchar(10) NOT NULL, `networkSpeed` varchar(10) NOT NULL, `networkHexValue` varchar(10) NOT NULL, `enforce` int(11) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `containerization_con_owner_id_494eb637_fk_websiteFu` (`owner_id`), CONSTRAINT `containerization_con_owner_id_494eb637_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_websites` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_dockerpackages` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `Name` varchar(100) NOT NULL, `CPUs` integer NOT NULL, `Ram` integer NOT NULL, `Bandwidth` longtext NOT NULL, `DiskSpace` longtext NOT NULL, `config` longtext NOT NULL);""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_dockersites` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `ComposePath` longtext NOT NULL, `SitePath` longtext NOT NULL, `MySQLPath` longtext NOT NULL, `state` integer NOT NULL, `SiteType` integer NOT NULL, `MySQLDBName` varchar(100) NOT NULL, `MySQLDBNUser` varchar(100) NOT NULL, `CPUsMySQL` varchar(100) NOT NULL, `MemoryMySQL` varchar(100) NOT NULL, `port` varchar(100) NOT NULL, `CPUsSite` varchar(100) NOT NULL, `MemorySite` varchar(100) NOT NULL, `SiteName` varchar(255) NOT NULL UNIQUE, `finalURL` longtext NOT NULL, `blogTitle` longtext NOT NULL, `adminUser` varchar(100) NOT NULL, `adminEmail` varchar(100) NOT NULL, `admin_id` integer NOT NULL);""" try: cursor.execute(query) except: pass query = "ALTER TABLE `websiteFunctions_dockersites` ADD CONSTRAINT `websiteFunctions_doc_admin_id_88f5cb6d_fk_websiteFu` FOREIGN KEY (`admin_id`) REFERENCES `websiteFunctions_websites` (`id`);" try: cursor.execute(query) except: pass query = "CREATE TABLE `websiteFunctions_packageassignment` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `package_id` integer NOT NULL, `user_id` integer NOT NULL);" try: cursor.execute(query) except: pass query = """ALTER TABLE `websiteFunctions_packageassignment` ADD CONSTRAINT `websiteFunctions_pac_package_id_420b6aff_fk_websiteFu` FOREIGN KEY (`package_id`) REFERENCES `websiteFunctions_dockerpackages` (`id`);""" try: cursor.execute(query) except: pass query = "ALTER TABLE `websiteFunctions_packageassignment` ADD CONSTRAINT `websiteFunctions_pac_user_id_864958ce_fk_loginSyst` FOREIGN KEY (`user_id`) REFERENCES `loginSystem_administrator` (`id`);" try: cursor.execute(query) except: pass query = """ALTER TABLE `websiteFunctions_dockersites` ADD CONSTRAINT `websiteFunctions_doc_admin_id_88f5cb6d_fk_websiteFu` FOREIGN KEY (`admin_id`) REFERENCES `websiteFunctions_websites` (`id`);""" try: cursor.execute(query) except: pass try: connection.close() except: pass except: pass @staticmethod def CLMigrations(): try: connection, cursor = Upgrade.setupConnection('cyberpanel') query = """CREATE TABLE `CLManager_clpackages` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `speed` varchar(50) NOT NULL, `vmem` varchar(50) NOT NULL, `pmem` varchar(50) NOT NULL, `io` varchar(50) NOT NULL, `iops` varchar(50) NOT NULL, `ep` varchar(50) NOT NULL, `nproc` varchar(50) NOT NULL, `inodessoft` varchar(50) NOT NULL, `inodeshard` varchar(50) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `CLManager_clpackages_owner_id_9898c1e8_fk_packages_package_id` (`owner_id`), CONSTRAINT `CLManager_clpackages_owner_id_9898c1e8_fk_packages_package_id` FOREIGN KEY (`owner_id`) REFERENCES `packages_package` (`id`) )""" try: cursor.execute(query) except: pass query = "ALTER TABLE packages_package ADD COLUMN allowFullDomain INT DEFAULT 1;" try: cursor.execute(query) except: pass query = "ALTER TABLE packages_package ADD COLUMN enforceDiskLimits INT DEFAULT 0;" try: cursor.execute(query) except: pass try: connection.close() except: pass except: pass @staticmethod def manageServiceMigrations(): try: connection, cursor = Upgrade.setupConnection('cyberpanel') query = """CREATE TABLE `manageServices_pdnsstatus` ( `id` int(11) NOT NULL AUTO_INCREMENT, `serverStatus` int(11) NOT NULL, `type` varchar(6) NOT NULL, PRIMARY KEY (`id`) )""" try: cursor.execute(query) except: pass try: cursor.execute('alter table manageServices_pdnsstatus add masterServer varchar(200)') except: pass try: cursor.execute('alter table manageServices_pdnsstatus add masterIP varchar(200)') except: pass try: cursor.execute('ALTER TABLE `manageServices_pdnsstatus` CHANGE `type` `type` VARCHAR(10) NULL;') except: pass query = '''CREATE TABLE `databases_dbmeta` ( `id` int(11) NOT NULL AUTO_INCREMENT, `key` varchar(200) NOT NULL, `value` longtext NOT NULL, `database_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `databases_dbmeta_database_id_777997bc_fk_databases_databases_id` (`database_id`), CONSTRAINT `databases_dbmeta_database_id_777997bc_fk_databases_databases_id` FOREIGN KEY (`database_id`) REFERENCES `databases_databases` (`id`) )''' try: cursor.execute(query) except: pass query = """CREATE TABLE `filemanager_trash` ( `id` int(11) NOT NULL AUTO_INCREMENT, `originalPath` varchar(500) NOT NULL, `fileName` varchar(200) NOT NULL, `website_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `filemanager_trash_website_id_e2762f3c_fk_websiteFu` (`website_id`), CONSTRAINT `filemanager_trash_website_id_e2762f3c_fk_websiteFu` FOREIGN KEY (`website_id`) REFERENCES `websiteFunctions_websites` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `databases_globaluserdb` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(200) NOT NULL, `password` varchar(500) NOT NULL, `token` varchar(20) NOT NULL, PRIMARY KEY (`id`) )""" try: cursor.execute(query) except: pass query = "CREATE TABLE `databases_databasesusers` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `username` varchar(50) NOT NULL UNIQUE, `owner_id` integer NOT NULL)" try: cursor.execute(query) except: pass query = "ALTER TABLE `databases_databasesusers` ADD CONSTRAINT `databases_databasesu_owner_id_908fc638_fk_databases` FOREIGN KEY (`owner_id`) REFERENCES `databases_databases` (`id`);" try: cursor.execute(query) except: pass try: connection.close() except: pass except: pass @staticmethod def _python_for_manage(): """Resolve Python for manage.py (avoid FileNotFoundError when /usr/local/CyberPanel/bin/python missing).""" for path in ('/usr/local/CyberPanel/bin/python', '/usr/local/CyberCP/bin/python', '/usr/bin/python3', '/usr/local/bin/python3'): if path and os.path.isfile(path) and os.access(path, os.X_OK): return path return '/usr/bin/python3' @staticmethod def GeneralMigrations(): try: cwd = os.getcwd() os.chdir('/usr/local/CyberCP') py = Upgrade._python_for_manage() command = py + ' manage.py makemigrations' Upgrade.executioner(command, 'python manage.py makemigrations', 0) command = py + ' manage.py makemigrations' Upgrade.executioner(command, py + ' manage.py migrate', 0) os.chdir(cwd) except: pass @staticmethod def fixBaseTemplateMigrations(): """ Fix baseTemplate migrations to prevent NodeNotFoundError on AlmaLinux 9 and Ubuntu 24 """ try: Upgrade.stdOut("Fixing baseTemplate migrations for AlmaLinux 9 and Ubuntu 24 compatibility...") # Ensure baseTemplate migrations directory exists migrations_dir = "/usr/local/CyberCP/baseTemplate/migrations" if not os.path.exists(migrations_dir): os.makedirs(migrations_dir) Upgrade.stdOut("Created baseTemplate migrations directory") # Create __init__.py if it doesn't exist init_file = os.path.join(migrations_dir, "__init__.py") if not os.path.exists(init_file): with open(init_file, 'w') as f: f.write("") Upgrade.stdOut("Created baseTemplate migrations __init__.py") # Create 0001_initial.py if it doesn't exist initial_migration = os.path.join(migrations_dir, "0001_initial.py") if not os.path.exists(initial_migration): initial_content = '''# Generated by Django 3.2.25 on 2024-01-01 00:00 from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='CyberPanelCosmetic', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('MainDashboardCSS', models.TextField(default='')), ], ), migrations.CreateModel( name='UserNotificationPreferences', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('backup_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the backup notification')), ('ai_scanner_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the AI scanner notification')), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ], options={ 'verbose_name': 'User Notification Preferences', 'verbose_name_plural': 'User Notification Preferences', }, ), migrations.CreateModel( name='version', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('version', models.CharField(max_length=10)), ('build', models.IntegerField()), ], ), ] ''' with open(initial_migration, 'w') as f: f.write(initial_content) Upgrade.stdOut("Created baseTemplate 0001_initial.py migration") # Create 0002_usernotificationpreferences.py if it doesn't exist notification_migration = os.path.join(migrations_dir, "0002_usernotificationpreferences.py") if not os.path.exists(notification_migration): notification_content = '''# Generated by Django 3.2.25 on 2024-01-01 00:01 from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ ('baseTemplate', '0001_initial'), ('loginSystem', '0001_initial'), ] operations = [ migrations.AddField( model_name='usernotificationpreferences', name='user', field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='notification_preferences', to='loginSystem.administrator'), ), ] ''' with open(notification_migration, 'w') as f: f.write(notification_content) Upgrade.stdOut("Created baseTemplate 0002_usernotificationpreferences.py migration") # Set proper permissions command = "chown -R root:root " + migrations_dir Upgrade.executioner(command, 0) command = "chmod -R 755 " + migrations_dir Upgrade.executioner(command, 0) # Update Django settings to include DEFAULT_AUTO_FIELD if not present settings_file = "/usr/local/CyberCP/CyberCP/settings.py" if os.path.exists(settings_file): with open(settings_file, 'r') as f: settings_content = f.read() if "DEFAULT_AUTO_FIELD" not in settings_content: with open(settings_file, 'a') as f: f.write("\n# Default primary key field type\n") f.write("# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field\n") f.write("DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'\n") Upgrade.stdOut("Added DEFAULT_AUTO_FIELD to Django settings") Upgrade.stdOut("baseTemplate migrations fixed successfully") except Exception as e: Upgrade.stdOut("Error fixing baseTemplate migrations: " + str(e)) @staticmethod def migrateRainloopToSnappymail(): """ Migrate data from old rainloop folder to new snappymail folder This migration is for upgrading from CyberPanel 2.4.4 to 2.5.5-dev """ try: old_data_path = '/usr/local/lscp/cyberpanel/rainloop/data' new_data_path = '/usr/local/lscp/cyberpanel/snappymail/data' # Check if old rainloop data exists if not os.path.exists(old_data_path): Upgrade.stdOut("No old rainloop data found, skipping migration.", 0) return 0 # Check if old data directory has actual content try: old_data_contents = os.listdir(old_data_path) if not old_data_contents or old_data_contents == []: Upgrade.stdOut("Old rainloop data directory is empty, skipping migration.", 0) return 0 except: Upgrade.stdOut("Could not read old rainloop data directory, skipping migration.", 0) return 0 # Check if new snappymail data already exists and has content if os.path.exists(new_data_path): try: new_data_contents = os.listdir(new_data_path) # If new directory has content (more than just empty subdirs), don't migrate if new_data_contents and len(new_data_contents) > 0: # Check if _data_ directory exists and has content data_dir = os.path.join(new_data_path, '_data_') if os.path.exists(data_dir): default_dir = os.path.join(data_dir, '_default_') if os.path.exists(default_dir): default_contents = os.listdir(default_dir) # If configs, domains, or storage exist, assume migration already done if any(item in default_contents for item in ['configs', 'domains', 'storage']): Upgrade.stdOut("SnappyMail data already exists, skipping migration.", 0) return 0 except: pass Upgrade.stdOut("Migrating rainloop data to snappymail...", 0) # Ensure new data directory structure exists os.makedirs(new_data_path, exist_ok=True) os.makedirs(os.path.join(new_data_path, '_data_', '_default_'), exist_ok=True) # Use rsync to copy data (preserves permissions, ownership, and handles large files) import subprocess import shlex # Copy all data from old to new location command = f'rsync -av --ignore-existing {old_data_path}/ {new_data_path}/' cmd = shlex.split(command) result = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if result == 0: # Set proper ownership for migrated data command = "chown -R lscpd:lscpd " + new_data_path Upgrade.executioner_silent(command, 'Set ownership for migrated data', 0) # Set proper permissions command = "chmod -R 775 " + new_data_path Upgrade.executioner_silent(command, 'Set permissions for migrated data', 0) Upgrade.stdOut("Successfully migrated rainloop data to snappymail.", 0) # Update include.php to use new snappymail path include_file = '/usr/local/CyberCP/public/snappymail/include.php' if os.path.exists(include_file): try: with open(include_file, 'r') as f: content = f.read() # Replace rainloop path with snappymail path content = content.replace( '/usr/local/lscp/cyberpanel/rainloop/data', '/usr/local/lscp/cyberpanel/snappymail/data' ) with open(include_file, 'w') as f: f.write(content) Upgrade.stdOut("Updated include.php to use snappymail data path.", 0) except Exception as e: Upgrade.stdOut(f"Warning: Could not update include.php: {str(e)}", 0) # Also update the version-specific include.php if it exists try: iPath = os.listdir('/usr/local/CyberCP/public/snappymail/snappymail/v/') if iPath: version_include = f"/usr/local/CyberCP/public/snappymail/snappymail/v/{iPath[0]}/include.php" if os.path.exists(version_include): with open(version_include, 'r') as f: content = f.read() # Replace rainloop path with snappymail path content = content.replace( '/usr/local/lscp/cyberpanel/rainloop/data', '/usr/local/lscp/cyberpanel/snappymail/data' ) with open(version_include, 'w') as f: f.write(content) Upgrade.stdOut("Updated version-specific include.php to use snappymail data path.", 0) except: pass # Replace ALL rainloop path/URL references in migrated SnappyMail data (configs, domains, plugins) try: data_extensions = ('.ini', '.json', '.php', '.cfg') replace_count = 0 for root, _dirs, files in os.walk(new_data_path): for name in files: if name.endswith(data_extensions): path = os.path.join(root, name) try: with open(path, 'r', encoding='utf-8', errors='replace') as f: content = f.read() new_content = content.replace( '/usr/local/lscp/cyberpanel/rainloop/data', '/usr/local/lscp/cyberpanel/snappymail/data' ).replace( '/rainloop/', '/snappymail/' ).replace( 'rainloop/data', 'snappymail/data' ) if new_content != content: with open(path, 'w', encoding='utf-8') as f: f.write(new_content) replace_count += 1 except (IOError, OSError): pass if replace_count > 0: Upgrade.stdOut(f"Updated rainloop→snappymail links in {replace_count} config file(s).", 0) except Exception as e: Upgrade.stdOut(f"Warning: Could not replace rainloop links in data files: {str(e)}", 0) # Redirect /rainloop to /snappymail so old bookmarks and links keep working try: htaccess_path = '/usr/local/CyberCP/public/.htaccess' redirect_block = ( '\n# Redirect old RainLoop URL to SnappyMail (2.5.5 upgrade)\n' '\n' 'RewriteEngine On\n' 'RewriteRule ^rainloop/?(.*)$ /snappymail/$1 [R=301,L]\n' '\n' ) if os.path.exists(htaccess_path): with open(htaccess_path, 'r', encoding='utf-8', errors='replace') as f: existing = f.read() if 'Redirect old RainLoop URL to SnappyMail' not in existing: with open(htaccess_path, 'a', encoding='utf-8') as f: f.write(redirect_block) Upgrade.stdOut("Added /rainloop→/snappymail redirect to .htaccess.", 0) else: with open(htaccess_path, 'w', encoding='utf-8') as f: f.write(redirect_block) Upgrade.stdOut("Created .htaccess with /rainloop→/snappymail redirect.", 0) except Exception as e: Upgrade.stdOut(f"Warning: Could not add rainloop redirect to .htaccess: {str(e)}", 0) return 1 else: Upgrade.stdOut("Warning: Data migration completed with errors. Please verify manually.", 0) return 0 except Exception as e: Upgrade.stdOut(f"Error during rainloop to snappymail migration: {str(e)}", 0) return 0 def IncBackupMigrations(): try: connection, cursor = Upgrade.setupConnection('cyberpanel') query = """CREATE TABLE `IncBackups_backupjob` ( `id` int(11) NOT NULL AUTO_INCREMENT, `destination` varchar(300) NOT NULL, `frequency` varchar(50) NOT NULL, `websiteData` int(11) NOT NULL, `websiteDatabases` int(11) NOT NULL, `websiteDataEmails` int(11) NOT NULL, PRIMARY KEY (`id`) )""" try: cursor.execute(query) except: pass query = 'ALTER TABLE IncBackups_backupjob ADD retention integer DEFAULT 0' try: cursor.execute(query) except: pass query = """CREATE TABLE `IncBackups_incjob` ( `id` int(11) NOT NULL AUTO_INCREMENT, `date` datetime(6) NOT NULL, `website_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `IncBackups_incjob_website_id_aad31bf6_fk_websiteFu` (`website_id`), CONSTRAINT `IncBackups_incjob_website_id_aad31bf6_fk_websiteFu` FOREIGN KEY (`website_id`) REFERENCES `websiteFunctions_websites` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `IncBackups_jobsites` ( `id` int(11) NOT NULL AUTO_INCREMENT, `website` varchar(300) NOT NULL, `job_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `IncBackups_jobsites_job_id_494a1f69_fk_IncBackups_backupjob_id` (`job_id`), CONSTRAINT `IncBackups_jobsites_job_id_494a1f69_fk_IncBackups_backupjob_id` FOREIGN KEY (`job_id`) REFERENCES `IncBackups_backupjob` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `IncBackups_jobsnapshots` ( `id` int(11) NOT NULL AUTO_INCREMENT, `type` varchar(300) NOT NULL, `snapshotid` varchar(50) NOT NULL, `job_id` int(11) NOT NULL, `destination` varchar(200) NOT NULL, PRIMARY KEY (`id`), KEY `IncBackups_jobsnapshots_job_id_a8237ca8_fk_IncBackups_incjob_id` (`job_id`), CONSTRAINT `IncBackups_jobsnapshots_job_id_a8237ca8_fk_IncBackups_incjob_id` FOREIGN KEY (`job_id`) REFERENCES `IncBackups_incjob` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_gitlogs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `date` datetime(6) NOT NULL, `type` varchar(5) NOT NULL, `message` longtext NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `websiteFunctions_git_owner_id_ce74c7de_fk_websiteFu` (`owner_id`), CONSTRAINT `websiteFunctions_git_owner_id_ce74c7de_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_websites` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_backupjob` ( `id` int(11) NOT NULL AUTO_INCREMENT, `logFile` varchar(1000) NOT NULL, `ipAddress` varchar(50) NOT NULL, `port` varchar(15) NOT NULL, `jobFailedSites` int(11) NOT NULL, `jobSuccessSites` int(11) NOT NULL, `location` int(11) NOT NULL, PRIMARY KEY (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_backupjoblogs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `message` longtext NOT NULL, `owner_id` int(11) NOT NULL, `status` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `websiteFunctions_bac_owner_id_af3d15f9_fk_websiteFu` (`owner_id`), CONSTRAINT `websiteFunctions_bac_owner_id_af3d15f9_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_backupjob` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_gdrive` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `auth` longtext NOT NULL, `runTime` varchar(20) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `websiteFunctions_gdr_owner_id_b5b1e86f_fk_loginSyst` (`owner_id`), CONSTRAINT `websiteFunctions_gdr_owner_id_b5b1e86f_fk_loginSyst` FOREIGN KEY (`owner_id`) REFERENCES `loginSystem_administrator` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_gdrivesites` ( `id` int(11) NOT NULL AUTO_INCREMENT, `domain` varchar(200) NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `websiteFunctions_gdr_owner_id_ff78b305_fk_websiteFu` (`owner_id`), CONSTRAINT `websiteFunctions_gdr_owner_id_ff78b305_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_gdrive` (`id`) )""" try: cursor.execute(query) except: pass query = """CREATE TABLE `websiteFunctions_gdrivejoblogs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `status` int(11) NOT NULL, `message` longtext NOT NULL, `owner_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `websiteFunctions_gdr_owner_id_4cf7983e_fk_websiteFu` (`owner_id`), CONSTRAINT `websiteFunctions_gdr_owner_id_4cf7983e_fk_websiteFu` FOREIGN KEY (`owner_id`) REFERENCES `websiteFunctions_gdrive` (`id`) )""" try: cursor.execute(query) except: pass query = "ALTER TABLE `websiteFunctions_childdomains` ADD `alais` INT NOT NULL DEFAULT '0' AFTER `master_id`; " try: cursor.execute(query) except: pass try: connection.close() except: pass except: pass @staticmethod def fixSubdomainLogConfigurations(): """Fix subdomain log configurations during upgrade""" try: # Check if this fix has already been applied fix_marker_file = '/usr/local/lscp/logs/subdomain_log_fix_applied' if os.path.exists(fix_marker_file): Upgrade.stdOut("Subdomain log fix already applied - skipping") return Upgrade.stdOut("=== FIXING SUBDOMAIN LOG CONFIGURATIONS ===") # Import required modules import sys import os sys.path.append('/usr/local/CyberCP') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") try: import django django.setup() from websiteFunctions.models import ChildDomains from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging from plogical.processUtilities import ProcessUtilities import re import shutil from datetime import datetime # Get all child domains child_domains = ChildDomains.objects.all() if not child_domains: Upgrade.stdOut("No child domains found - skipping subdomain log fix") return Upgrade.stdOut(f"Found {len(child_domains)} child domains to check") fixed_count = 0 skipped_count = 0 for child_domain in child_domains: domain_name = child_domain.domain master_domain = child_domain.master.domain vhost_conf_path = f"/usr/local/lsws/conf/vhosts/{domain_name}/vhost.conf" if not os.path.exists(vhost_conf_path): Upgrade.stdOut(f"⚠️ Skipping {domain_name}: vHost config not found") skipped_count += 1 continue try: # Read current configuration with open(vhost_conf_path, 'r') as f: config_content = f.read() # Check if fix is needed if f'{master_domain}.error_log' not in config_content and f'{master_domain}.access_log' not in config_content: Upgrade.stdOut(f"✅ {domain_name}: Already has correct log configuration") skipped_count += 1 continue # Create backup backup_path = f"{vhost_conf_path}.backup.{datetime.now().strftime('%Y%m%d_%H%M%S')}" shutil.copy2(vhost_conf_path, backup_path) # Fix the configuration fixed_content = config_content # Fix error log path fixed_content = re.sub( rf'errorlog\s+\$VH_ROOT/logs/{re.escape(master_domain)}\.error_log', f'errorlog $VH_ROOT/logs/{domain_name}.error_log', fixed_content ) # Fix access log path fixed_content = re.sub( rf'accesslog\s+\$VH_ROOT/logs/{re.escape(master_domain)}\.access_log', f'accesslog $VH_ROOT/logs/{domain_name}.access_log', fixed_content ) # Fix CustomLog paths (for Apache configurations) fixed_content = re.sub( rf'CustomLog\s+/home/{re.escape(master_domain)}/logs/{re.escape(master_domain)}\.access_log', f'CustomLog /home/{domain_name}/logs/{domain_name}.access_log', fixed_content ) # Write the fixed configuration with open(vhost_conf_path, 'w') as f: f.write(fixed_content) # Set proper ownership ProcessUtilities.executioner(f'chown lsadm:lsadm {vhost_conf_path}') # Create the log directory if it doesn't exist log_dir = f"/home/{master_domain}/logs" if not os.path.exists(log_dir): os.makedirs(log_dir, exist_ok=True) ProcessUtilities.executioner(f'chown -R {child_domain.master.externalApp}:{child_domain.master.externalApp} {log_dir}') # Create separate log files for the child domain error_log_path = f"{log_dir}/{domain_name}.error_log" access_log_path = f"{log_dir}/{domain_name}.access_log" # Create empty log files if they don't exist for log_path in [error_log_path, access_log_path]: if not os.path.exists(log_path): with open(log_path, 'w') as f: f.write('') ProcessUtilities.executioner(f'chown {child_domain.master.externalApp}:{child_domain.master.externalApp} {log_path}') ProcessUtilities.executioner(f'chmod 644 {log_path}') Upgrade.stdOut(f"✅ Fixed log configuration for {domain_name}") logging.writeToFile(f'Fixed subdomain log configuration for {domain_name} during upgrade') fixed_count += 1 except Exception as e: Upgrade.stdOut(f"❌ Failed to fix {domain_name}: {str(e)}") logging.writeToFile(f'Error fixing subdomain logs for {domain_name} during upgrade: {str(e)}') # Restart LiteSpeed to apply changes if any were made if fixed_count > 0: Upgrade.stdOut("Restarting LiteSpeed to apply log configuration changes...") ProcessUtilities.executioner('systemctl restart lsws') Upgrade.stdOut(f"=== SUBDOMAIN LOG FIX COMPLETE ===") Upgrade.stdOut(f"Fixed: {fixed_count} domains") Upgrade.stdOut(f"Skipped: {skipped_count} domains") # Create marker file to indicate fix has been applied try: with open(fix_marker_file, 'w') as f: f.write(f"Subdomain log fix applied on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") f.write(f"Fixed domains: {fixed_count}\n") f.write(f"Skipped domains: {skipped_count}\n") except: pass except ImportError as e: Upgrade.stdOut(f"⚠️ Django not available during upgrade: {str(e)}") Upgrade.stdOut("Subdomain log fix will be applied on next CyberPanel restart") except Exception as e: Upgrade.stdOut(f"❌ Error in subdomain log fix: {str(e)}") logging.writeToFile(f'Error in subdomain log fix during upgrade: {str(e)}') @staticmethod def enableServices(): try: servicePath = '/home/cyberpanel/powerdns' writeToFile = open(servicePath, 'w+') writeToFile.close() servicePath = '/home/cyberpanel/postfix' writeToFile = open(servicePath, 'w+') writeToFile.close() servicePath = '/home/cyberpanel/pureftpd' writeToFile = open(servicePath, 'w+') writeToFile.close() except: pass @staticmethod def backupCriticalFiles(): """Backup all critical configuration files before upgrade""" import tempfile backup_dir = tempfile.mkdtemp(prefix='cyberpanel_backup_') critical_files = [ '/usr/local/CyberCP/CyberCP/settings.py', '/usr/local/CyberCP/.git/config', # Git configuration '/usr/local/lsws/conf/httpd_config.conf', # OpenLiteSpeed config - critical for preventing port binding failures ] # Also backup any custom configurations custom_configs = [ '/usr/local/CyberCP/baseTemplate/static/baseTemplate/custom/', '/usr/local/CyberCP/public/phpmyadmin/config.inc.php', '/usr/local/lscp/cyberpanel/snappymail/data/_data_/', ] # Backup Imunify360 directories and configuration imunify_paths = [ '/usr/local/CyberCP/public/imunify', '/usr/local/CyberCP/public/imunifyav', '/etc/sysconfig/imunify360/integration.conf', ] for imunify_path in imunify_paths: if os.path.exists(imunify_path): if os.path.isdir(imunify_path): custom_configs.append(imunify_path) else: critical_files.append(imunify_path) backed_up_files = {} for file_path in critical_files: if os.path.exists(file_path): try: backup_path = os.path.join(backup_dir, os.path.basename(file_path)) shutil.copy2(file_path, backup_path) backed_up_files[file_path] = backup_path Upgrade.stdOut(f"Backed up {file_path}") except Exception as e: Upgrade.stdOut(f"Failed to backup {file_path}: {str(e)}") # Backup directories for dir_path in custom_configs: if os.path.exists(dir_path): try: backup_path = os.path.join(backup_dir, os.path.basename(dir_path)) shutil.copytree(dir_path, backup_path) backed_up_files[dir_path] = backup_path Upgrade.stdOut(f"Backed up directory {dir_path}") except Exception as e: Upgrade.stdOut(f"Failed to backup {dir_path}: {str(e)}") return backup_dir, backed_up_files @staticmethod def restoreCriticalFiles(backup_dir, backed_up_files): """Restore critical configuration files after upgrade""" for original_path, backup_path in backed_up_files.items(): # Skip settings.py - we'll handle it separately to preserve INSTALLED_APPS if 'settings.py' in original_path: Upgrade.stdOut(f"Skipping {original_path} - will be handled separately") continue try: if os.path.isdir(backup_path): if os.path.exists(original_path): shutil.rmtree(original_path) shutil.copytree(backup_path, original_path) else: # Create directory if it doesn't exist os.makedirs(os.path.dirname(original_path), exist_ok=True) shutil.copy2(backup_path, original_path) Upgrade.stdOut(f"Restored {original_path}") except Exception as e: Upgrade.stdOut(f"Failed to restore {original_path}: {str(e)}") @staticmethod def downloadAndUpgrade(versionNumbring, branch): try: ## Download latest version. ## Backup all critical files Upgrade.stdOut("Backing up critical configuration files...") backup_dir, backed_up_files = Upgrade.backupCriticalFiles() ## CyberPanel DB Creds dbName = settings.DATABASES['default']['NAME'] dbUser = settings.DATABASES['default']['USER'] password = settings.DATABASES['default']['PASSWORD'] host = settings.DATABASES['default']['HOST'] port = settings.DATABASES['default']['PORT'] ## Root DB Creds rootdbName = settings.DATABASES['rootdb']['NAME'] rootdbdbUser = settings.DATABASES['rootdb']['USER'] rootdbpassword = settings.DATABASES['rootdb']['PASSWORD'] ## Complete db string completDBString = """\nDATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': '%s', 'USER': '%s', 'PASSWORD': '%s', 'HOST': '%s', 'PORT':'%s' }, 'rootdb': { 'ENGINE': 'django.db.backends.mysql', 'NAME': '%s', 'USER': '%s', 'PASSWORD': '%s', 'HOST': '%s', 'PORT': '%s', }, }\n""" % (dbName, dbUser, password, host, port, rootdbName, rootdbdbUser, rootdbpassword, host, port) settingsFile = '/usr/local/CyberCP/CyberCP/settings.py' Upgrade.stdOut("Critical files backed up to: " + backup_dir) ## Always do a fresh clone for clean upgrade Upgrade.stdOut("Performing clean upgrade by removing and re-cloning CyberPanel...") # Set git config first command = 'git config --global user.email "support@cyberpanel.net"' if not Upgrade.executioner(command, command, 1): return 0, 'Failed to execute %s' % (command) command = 'git config --global user.name "CyberPanel"' if not Upgrade.executioner(command, command, 1): return 0, 'Failed to execute %s' % (command) # Change to parent directory os.chdir('/usr/local') # Remove old CyberCP directory if os.path.exists('CyberCP'): Upgrade.stdOut("Removing old CyberCP directory...") try: shutil.rmtree('CyberCP') Upgrade.stdOut("Old CyberCP directory removed successfully.") except Exception as e: Upgrade.stdOut(f"Error removing CyberCP directory: {str(e)}") # Try to restore backup if removal fails Upgrade.restoreCriticalFiles(backup_dir, backed_up_files) return 0, 'Failed to remove old CyberCP directory' # Clone the new repository (use CYBERPANEL_GIT_USER for fork, e.g. master3395) git_user = os.environ.get('CYBERPANEL_GIT_USER', 'master3395') Upgrade.stdOut("Cloning fresh CyberPanel repository...") command = 'git clone https://github.com/%s/cyberpanel CyberCP' % git_user if not Upgrade.executioner(command, command, 1): # Try to restore backup if clone fails Upgrade.stdOut("Clone failed, attempting to restore backup...") Upgrade.restoreCriticalFiles(backup_dir, backed_up_files) return 0, 'Failed to clone CyberPanel repository' # Checkout the correct branch os.chdir('/usr/local/CyberCP') command = 'git checkout %s' % (branch) if not Upgrade.executioner(command, command, 1): Upgrade.stdOut(f"Warning: Failed to checkout branch {branch}, continuing with default branch") # Restore all backed up configuration files (except settings.py) Upgrade.stdOut("Restoring configuration files...") Upgrade.restoreCriticalFiles(backup_dir, backed_up_files) ## Handle settings.py separately to preserve NEW INSTALLED_APPS while keeping old database credentials # Read the NEW settings file from the fresh clone (has new INSTALLED_APPS like 'aiScanner') settingsData = open(settingsFile, 'r').read() # Replace only the DATABASES section with our saved credentials import re # More precise pattern to match the entire DATABASES dictionary including nested dictionaries # This pattern looks for DATABASES = { ... } including the 'default' and 'rootdb' nested dicts database_pattern = r'DATABASES\s*=\s*\{[^}]*\{[^}]*\}[^}]*\{[^}]*\}[^}]*\}' # Replace the DATABASES section with our saved credentials from before upgrade settingsData = re.sub(database_pattern, completDBString.strip(), settingsData, flags=re.DOTALL) # Write back the updated settings writeToFile = open(settingsFile, 'w') writeToFile.write(settingsData) writeToFile.close() Upgrade.stdOut('Settings file updated with database credentials while preserving new INSTALLED_APPS!') Upgrade.staticContent() # Restore Imunify360 after upgrade Upgrade.restoreImunify360() # FINAL STEP: Ensure Imunify360 execute permissions are set Upgrade.finalImunifyPermissions() return 1, None except Exception as e: ErrorSanitizer.log_error_securely(e, 'installLSCPD') return 0, "Failed to install LSCPD" @staticmethod def installLSCPD(branch): try: if Upgrade.SoftUpgrade == 0: Upgrade.stdOut("Starting LSCPD installation..") cwd = os.getcwd() os.chdir('/usr/local') command = 'yum -y install gcc gcc-c++ make autoconf glibc rcs' Upgrade.executioner(command, 'LSCPD Pre-reqs [one]', 0) ## lscpdPath = '/usr/local/lscp/bin/lscpd' if os.path.exists(lscpdPath): os.remove(lscpdPath) try: try: result = subprocess.run('uname -a', capture_output=True, universal_newlines=True, shell=True) except: result = subprocess.run('uname -a', stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) if result.stdout.find('aarch64') == -1: lscpdSelection = 'lscpd-0.3.1' if os.path.exists(Upgrade.UbuntuPath): result = open(Upgrade.UbuntuPath, 'r').read() if result.find('22.04') > -1 or result.find('24.04') > -1: lscpdSelection = 'lscpd.0.4.0' else: lscpdSelection = 'lscpd.aarch64' except: lscpdSelection = 'lscpd-0.3.1' if os.path.exists(Upgrade.UbuntuPath): result = open(Upgrade.UbuntuPath, 'r').read() if result.find('22.04') > -1 or result.find('24.04') > -1: lscpdSelection = 'lscpd.0.4.0' command = f'cp -f /usr/local/CyberCP/{lscpdSelection} /usr/local/lscp/bin/{lscpdSelection}' Upgrade.executioner(command, command, 0) command = 'rm -f /usr/local/lscp/bin/lscpd' Upgrade.executioner(command, command, 0) command = f'mv /usr/local/lscp/bin/{lscpdSelection} /usr/local/lscp/bin/lscpd' Upgrade.executioner(command, command, 0) command = f'chmod 755 {lscpdPath}' Upgrade.executioner(command, 'LSCPD Download.', 0) command = 'yum -y install pcre-devel openssl-devel expat-devel geoip-devel zlib-devel udns-devel which curl' Upgrade.executioner(command, 'LSCPD Pre-reqs [two]', 0) try: pwd.getpwnam('lscpd') except KeyError: command = 'adduser lscpd -M -d /usr/local/lscp' Upgrade.executioner(command, 'Add user LSCPD', 0) try: grp.getgrnam('lscpd') except KeyError: command = 'groupadd lscpd' Upgrade.executioner(command, 'Add group LSCPD', 0) command = 'usermod -a -G lscpd lscpd' Upgrade.executioner(command, 'Add group LSCPD', 0) command = 'usermod -a -G lsadm lscpd' Upgrade.executioner(command, 'Add group LSCPD', 0) command = 'systemctl daemon-reload' Upgrade.executioner(command, 'daemon-reload LSCPD', 0) command = 'systemctl restart lscpd' Upgrade.executioner(command, 'Restart LSCPD', 0) os.chdir(cwd) Upgrade.stdOut("LSCPD successfully installed!") except Exception as e: ErrorSanitizer.log_error_securely(e, 'installLSCPD') Upgrade.stdOut("Failed to install LSCPD [installLSCPD]") ### disable dkim signing in rspamd in ref to https://github.com/usmannasir/cyberpanel/issues/1176 @staticmethod def FixRSPAMDConfig(): RSPAMDConf = '/etc/rspamd' postfixConf = '/etc/postfix/main.cf' if os.path.exists(RSPAMDConf): DKIMPath = '/etc/rspamd/local.d/dkim_signing.conf' WriteToFile = open(DKIMPath, 'w') WriteToFile.write('enabled = false;\n') WriteToFile.close() if os.path.exists(postfixConf): appendpath = "/etc/postfix/main.cf" lines = open(appendpath, 'r').readlines() WriteToFile = open(appendpath, 'w') for line in lines: if line.find('smtpd_milters') > -1: continue elif line.find('non_smtpd_milters') > -1: continue elif line.find('milter_default_action') > -1: continue else: WriteToFile.write(line) RSPAMDConfContent = ''' ### Please do not edit this line, editing this line could break configurations smtpd_milters = inet:127.0.0.1:8891, inet:127.0.0.1:11332 non_smtpd_milters = $smtpd_milters milter_default_action = accept ''' WriteToFile.write(RSPAMDConfContent) WriteToFile.close() command = 'systemctl restart postfix && systemctl restart rspamd' Upgrade.executioner(command, 'postfix and rspamd restart', 0, True) #### if you update this function needs to update this function on plogical.acl.py as well @staticmethod def fixPermissions(): try: try: def generate_pass(length=14): chars = string.ascii_uppercase + string.ascii_lowercase + string.digits size = length return ''.join(random.choice(chars) for x in range(size)) content = """SetPassword('%s'); echo $oConfig->Save() ? 'Done' : 'Error'; ?>""" % (generate_pass()) writeToFile = open('/usr/local/CyberCP/public/snappymail.php', 'w') writeToFile.write(content) writeToFile.close() command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/data" subprocess.call(shlex.split(command)) except: pass Upgrade.stdOut("Fixing permissions..") command = "usermod -G lscpd,lsadm,nobody lscpd" Upgrade.executioner(command, 'chown core code', 0) command = "usermod -G lscpd,lsadm,nogroup lscpd" Upgrade.executioner(command, 'chown core code', 0) ###### fix Core CyberPanel permissions command = "find /usr/local/CyberCP -type d -exec chmod 0755 {} \;" Upgrade.executioner(command, 'chown core code', 0) command = "find /usr/local/CyberCP -type f -exec chmod 0644 {} \;" Upgrade.executioner(command, 'chown core code', 0) command = "chmod -R 755 /usr/local/CyberCP/bin" Upgrade.executioner(command, 'chown core code', 0) ## change owner command = "chown -R root:root /usr/local/CyberCP" Upgrade.executioner(command, 'chown core code', 0) ########### Fix LSCPD command = "find /usr/local/lscp -type d -exec chmod 0755 {} \;" Upgrade.executioner(command, 'chown core code', 0) command = "find /usr/local/lscp -type f -exec chmod 0644 {} \;" Upgrade.executioner(command, 'chown core code', 0) command = "chmod -R 755 /usr/local/lscp/bin" Upgrade.executioner(command, 'chown core code', 0) command = "chmod -R 755 /usr/local/lscp/fcgi-bin" Upgrade.executioner(command, 'chown core code', 0) command = "chown -R lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/tmp" Upgrade.executioner(command, 'chown core code', 0) ## change owner command = "chown -R root:root /usr/local/lscp" Upgrade.executioner(command, 'chown core code', 0) command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail" Upgrade.executioner(command, 'chown core code', 0) command = "chmod 700 /usr/local/CyberCP/cli/cyberPanel.py" Upgrade.executioner(command, 'chown core code', 0) command = "chmod 700 /usr/local/CyberCP/plogical/upgradeCritical.py" Upgrade.executioner(command, 'chown core code', 0) command = "chmod 755 /usr/local/CyberCP/postfixSenderPolicy/client.py" Upgrade.executioner(command, 'chown core code', 0) command = "chmod 640 /usr/local/CyberCP/CyberCP/settings.py" Upgrade.executioner(command, 'chown core code', 0) command = "chown root:cyberpanel /usr/local/CyberCP/CyberCP/settings.py" Upgrade.executioner(command, 'chown core code', 0) command = 'chmod +x /usr/local/CyberCP/CLManager/CLPackages.py' Upgrade.executioner(command, 'chmod CLPackages', 0) files = ['/etc/yum.repos.d/MariaDB.repo', '/etc/pdns/pdns.conf', '/etc/systemd/system/lscpd.service', '/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf', '/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf', '/etc/dovecot/dovecot.conf', '/usr/local/lsws/conf/httpd_config.xml', '/usr/local/lsws/conf/modsec.conf', '/usr/local/lsws/conf/httpd.conf'] for items in files: command = 'chmod 644 %s' % (items) Upgrade.executioner(command, 'chown core code', 0) impFile = ['/etc/pure-ftpd/pure-ftpd.conf', '/etc/pure-ftpd/pureftpd-pgsql.conf', '/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/pureftpd-ldap.conf', '/etc/dovecot/dovecot.conf', '/etc/pdns/pdns.conf', '/etc/pure-ftpd/db/mysql.conf', '/etc/powerdns/pdns.conf'] for items in impFile: command = 'chmod 600 %s' % (items) Upgrade.executioner(command, 'chown core code', 0) command = 'chmod 640 /etc/postfix/*.cf' subprocess.call(command, shell=True) command = 'chmod 640 /etc/dovecot/*.conf' subprocess.call(command, shell=True) command = 'chmod 640 /etc/dovecot/dovecot-sql.conf.ext' subprocess.call(command, shell=True) fileM = ['/usr/local/lsws/FileManager/', '/usr/local/CyberCP/install/FileManager', '/usr/local/CyberCP/serverStatus/litespeed/FileManager', '/usr/local/lsws/Example/html/FileManager'] for items in fileM: try: shutil.rmtree(items) except: pass command = 'chmod 755 /etc/pure-ftpd/' subprocess.call(command, shell=True) command = 'chmod 644 /etc/dovecot/dovecot.conf' subprocess.call(command, shell=True) command = 'chmod 644 /etc/postfix/main.cf' subprocess.call(command, shell=True) command = 'chmod 644 /etc/postfix/dynamicmaps.cf' subprocess.call(command, shell=True) command = 'chmod +x /usr/local/CyberCP/plogical/renew.py' Upgrade.executioner(command, command, 0) command = 'chmod +x /usr/local/CyberCP/CLManager/CLPackages.py' Upgrade.executioner(command, command, 0) clScripts = ['/usr/local/CyberCP/CLScript/panel_info.py', '/usr/local/CyberCP/CLScript/CloudLinuxPackages.py', '/usr/local/CyberCP/CLScript/CloudLinuxUsers.py', '/usr/local/CyberCP/CLScript/CloudLinuxDomains.py' , '/usr/local/CyberCP/CLScript/CloudLinuxResellers.py', '/usr/local/CyberCP/CLScript/CloudLinuxAdmins.py', '/usr/local/CyberCP/CLScript/CloudLinuxDB.py', '/usr/local/CyberCP/CLScript/UserInfo.py'] for items in clScripts: command = 'chmod +x %s' % (items) Upgrade.executioner(command, 0) command = 'chmod 600 /usr/local/CyberCP/plogical/adminPass.py' Upgrade.executioner(command, 0) command = 'chmod 600 /etc/cagefs/exclude/cyberpanelexclude' Upgrade.executioner(command, 0) command = "find /usr/local/CyberCP/ -name '*.pyc' -delete" Upgrade.executioner(command, 0) if os.path.exists(Upgrade.CentOSPath) or os.path.exists(Upgrade.openEulerPath): command = 'chown root:pdns /etc/pdns/pdns.conf' Upgrade.executioner(command, 0) command = 'chmod 640 /etc/pdns/pdns.conf' Upgrade.executioner(command, 0) else: command = 'chown root:pdns /etc/powerdns/pdns.conf' Upgrade.executioner(command, 0) command = 'chmod 640 /etc/powerdns/pdns.conf' Upgrade.executioner(command, 0) command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log' Upgrade.executioner(command, 0) command = '/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/public/snappymail.php' Upgrade.executioner_silent(command, 'Configure SnappyMail') command = 'chmod 600 /usr/local/CyberCP/public/snappymail.php' Upgrade.executioner_silent(command, 'Secure SnappyMail config') ### WriteToFile = open('/etc/fstab', 'a') WriteToFile.write('proc /proc proc defaults,hidepid=2 0 0\n') WriteToFile.close() command = 'mount -o remount,rw,hidepid=2 /proc' Upgrade.executioner(command, 0) ### CentOSPath = '/etc/redhat-release' openEulerPath = '/etc/openEuler-release' if not os.path.exists(CentOSPath) or not os.path.exists(openEulerPath): group = 'nobody' else: group = 'nogroup' command = 'chown root:%s /usr/local/lsws/logs' % (group) Upgrade.executioner(command, 0) command = 'chmod 750 /usr/local/lsws/logs' Upgrade.executioner(command, 0) ## symlink protection writeToFile = open('/usr/lib/sysctl.d/50-default.conf', 'a') writeToFile.writelines('fs.protected_hardlinks = 1\n') writeToFile.writelines('fs.protected_symlinks = 1\n') writeToFile.close() command = 'sysctl --system' Upgrade.executioner(command, 0) command = 'chmod 700 %s' % ('/home/cyberpanel') Upgrade.executioner(command, 0) destPrivKey = "/usr/local/lscp/conf/key.pem" command = 'chmod 600 %s' % (destPrivKey) Upgrade.executioner(command, 0) Upgrade.stdOut("Permissions updated.") except Exception as e: ErrorSanitizer.log_error_securely(e, 'fixPermissions') Upgrade.stdOut("Failed to fix permissions [fixPermissions]") @staticmethod def AutoUpgradeAcme(): command = '/root/.acme.sh/acme.sh --upgrade --auto-upgrade' Upgrade.executioner(command, command, 0) command = '/root/.acme.sh/acme.sh --set-default-ca --server letsencrypt' Upgrade.executioner(command, command, 0) @staticmethod def check_package_availability(package_name): """Check if a package is available in the repositories""" try: # Try to search for the package without installing if os.path.exists('/etc/yum.repos.d/') or os.path.exists('/etc/dnf/dnf.conf'): # RHEL-based systems command = f"dnf search --quiet {package_name} 2>/dev/null | grep -q '^Last metadata expiration' || yum search --quiet {package_name} 2>/dev/null | head -1" result = subprocess.run(command, shell=True, capture_output=True, text=True) return result.returncode == 0 else: # Ubuntu/Debian systems command = f"apt-cache search {package_name} 2>/dev/null | head -1" result = subprocess.run(command, shell=True, capture_output=True, text=True) return result.returncode == 0 and result.stdout.strip() != "" except Exception as e: Upgrade.stdOut(f"Error checking package availability for {package_name}: {str(e)}", 0) return False @staticmethod def is_almalinux9(): """Check if running on AlmaLinux 9""" if os.path.exists('/etc/almalinux-release'): try: with open('/etc/almalinux-release', 'r') as f: content = f.read() return 'release 9' in content except: return False return False @staticmethod def fix_almalinux9_mariadb(): """Fix AlmaLinux 9 MariaDB installation issues""" if not Upgrade.is_almalinux9(): return Upgrade.stdOut("Applying AlmaLinux 9 MariaDB fixes...", 1) try: # CRITICAL: Remove MariaDB-server-compat* before any MariaDB install (conflicts with 11.x) Upgrade.stdOut("Removing conflicting MariaDB-server-compat packages...", 1) try: # Multiple aggressive removal attempts to ensure compat package is gone # Step 1: Try dnf remove with allowerasing subprocess.run("dnf remove -y --allowerasing 'MariaDB-server-compat*' 2>/dev/null || true", shell=True, timeout=60) # Step 2: Force remove with rpm subprocess.run("rpm -e --nodeps MariaDB-server-compat-12.1.2-1.el9.noarch 2>/dev/null; true", shell=True, timeout=30) # cleanup if present from previous 12.1 # Step 3: Find and remove any remaining compat packages r = subprocess.run("rpm -qa 2>/dev/null | grep -i MariaDB-server-compat", shell=True, capture_output=True, text=True, timeout=30) for line in (r.stdout or "").strip().splitlines(): pkg = (line.strip().split() or [""])[0] if pkg and "MariaDB-server-compat" in pkg: Upgrade.stdOut(f"Force removing remaining compat package: {pkg}", 1) subprocess.run(["rpm", "-e", "--nodeps", pkg], timeout=30) # Step 4: Verify removal and exclude from future installs r = subprocess.run("rpm -qa 2>/dev/null | grep -i MariaDB-server-compat", shell=True, capture_output=True, text=True, timeout=30) if r.stdout.strip(): Upgrade.stdOut(f"Warning: Some compat packages still present: {r.stdout.strip()}", 0) # Add to dnf exclude to prevent reinstallation subprocess.run("dnf config-manager --setopt exclude='MariaDB-server-compat*' --save 2>/dev/null || true", shell=True, timeout=30) else: Upgrade.stdOut("Successfully removed all MariaDB-server-compat packages", 1) except Exception as e: Upgrade.stdOut("Warning: compat cleanup: " + str(e), 0) # Disable problematic MariaDB MaxScale repository Upgrade.stdOut("Disabling problematic MariaDB MaxScale repository...", 1) command = "dnf config-manager --disable mariadb-maxscale 2>/dev/null || true" subprocess.run(command, shell=True, capture_output=True) # Remove problematic repository files Upgrade.stdOut("Removing problematic repository files...", 1) problematic_repos = [ '/etc/yum.repos.d/mariadb-maxscale.repo', '/etc/yum.repos.d/mariadb-maxscale.repo.rpmnew' ] for repo_file in problematic_repos: if os.path.exists(repo_file): os.remove(repo_file) Upgrade.stdOut(f"Removed {repo_file}", 1) # Clean DNF cache Upgrade.stdOut("Cleaning DNF cache...", 1) command = "dnf clean all" subprocess.run(command, shell=True, capture_output=True) # Install MariaDB from official repository (version from /etc/cyberpanel/mariadb_version or default 11.8) mariadb_ver = "11.8" try: mariadb_version_file = "/etc/cyberpanel/mariadb_version" if os.path.isfile(mariadb_version_file): with open(mariadb_version_file, "r") as f: raw = f.read().strip() if raw in ("11.8", "12.1"): mariadb_ver = raw except Exception: pass Upgrade.stdOut("Setting up official MariaDB %s repository..." % mariadb_ver, 1) command = "curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash -s -- --mariadb-server-version='%s'" % mariadb_ver result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.returncode != 0: Upgrade.stdOut(f"Warning: MariaDB repo setup failed: {result.stderr}", 0) # Install MariaDB packages with exclude to prevent compat package conflicts Upgrade.stdOut("Installing MariaDB packages...", 1) mariadb_packages = "MariaDB-server MariaDB-client MariaDB-backup MariaDB-devel" # Use --exclude to prevent compat package from being installed command = f"dnf install -y --exclude='MariaDB-server-compat*' {mariadb_packages}" result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.returncode != 0: # Check if it's a compat package conflict error_output = result.stderr + result.stdout if "MariaDB-server-compat" in error_output or "conflicts" in error_output.lower(): Upgrade.stdOut("Compat package conflict detected, trying with --allowerasing...", 1) command = f"dnf install -y --allowerasing --exclude='MariaDB-server-compat*' {mariadb_packages}" result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.returncode != 0: Upgrade.stdOut(f"Error: MariaDB installation failed: {result.stderr}", 0) return False else: Upgrade.stdOut(f"Warning: MariaDB installation issues: {result.stderr}", 0) return False # Verify MariaDB was installed successfully if not os.path.exists('/usr/bin/mysql') and not os.path.exists('/usr/bin/mariadb'): Upgrade.stdOut("Error: MariaDB binaries not found after installation", 0) return False # Start and enable MariaDB service Upgrade.stdOut("Starting MariaDB service...", 1) services = ['mariadb', 'mysql', 'mysqld'] for service in services: try: command = f"systemctl start {service}" result = subprocess.run(command, shell=True, capture_output=True) if result.returncode == 0: command = f"systemctl enable {service}" subprocess.run(command, shell=True, capture_output=True) Upgrade.stdOut(f"MariaDB service started as {service}", 1) break except: continue Upgrade.stdOut("AlmaLinux 9 MariaDB fixes completed", 1) except Exception as e: Upgrade.stdOut(f"Error applying AlmaLinux 9 MariaDB fixes: {str(e)}", 0) @staticmethod def get_available_php_versions(): """Get list of available PHP versions based on OS""" # Check for AlmaLinux 9+ first if os.path.exists('/etc/almalinux-release'): try: with open('/etc/almalinux-release', 'r') as f: content = f.read() if 'release 9' in content or 'release 10' in content: Upgrade.stdOut("AlmaLinux 9+ detected - checking available PHP versions", 1) # AlmaLinux 9+ doesn't have PHP 7.1, 7.2, 7.3 php_versions = ['74', '80', '81', '82', '83', '84', '85'] else: php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85'] except: php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85'] else: # Check other OS versions os_info = Upgrade.findOperatingSytem() if os_info in [Ubuntu24, CENTOS8, Debian13]: php_versions = ['74', '80', '81', '82', '83', '84', '85'] else: php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85'] # Check availability of each version available_versions = [] for version in php_versions: if Upgrade.check_package_availability(f'lsphp{version}'): available_versions.append(version) else: Upgrade.stdOut(f"PHP {version} not available on this OS", 0) return available_versions @staticmethod def fixLiteSpeedConfig(): """Fix LiteSpeed configuration issues by creating missing files and fixing permissions""" try: Upgrade.stdOut("Checking and fixing LiteSpeed configuration...", 1) # Check if LiteSpeed is installed if not os.path.exists('/usr/local/lsws'): Upgrade.stdOut("LiteSpeed not found at /usr/local/lsws", 0) return # Fix LiteSpeed permissions first Upgrade.stdOut("Fixing LiteSpeed permissions...", 1) litespeed_dirs = ['/usr/local/lsws', '/usr/local/lscp', '/usr/local/CyberCP'] for directory in litespeed_dirs: if os.path.exists(directory): command = f'chown -R lscpd:lscpd {directory}' Upgrade.executioner(command, f'Fix ownership for {directory}', 0) command = f'chmod -R 755 {directory}' Upgrade.executioner(command, f'Fix permissions for {directory}', 0) # Create missing configuration files config_files = [ "/usr/local/lsws/conf/httpd_config.xml", "/usr/local/lsws/conf/httpd.conf", "/usr/local/lscp/conf/httpd_config.xml", "/usr/local/lscp/conf/httpd.conf" ] for config_file in config_files: if not os.path.exists(config_file): Upgrade.stdOut(f"Missing LiteSpeed config: {config_file}", 0) # Create directory if it doesn't exist os.makedirs(os.path.dirname(config_file), exist_ok=True) # Create minimal config file if config_file.endswith('httpd_config.xml'): with open(config_file, 'w') as f: f.write('\n') f.write('\n') f.write(' \n') f.write(' \n') f.write(' Default\n') f.write('
*:8088
\n') f.write('
\n') f.write('
\n') elif config_file.endswith('httpd.conf'): with open(config_file, 'w') as f: f.write('# Minimal LiteSpeed HTTP configuration\n') f.write('# This file will be updated by CyberPanel\n') # Set proper permissions os.chmod(config_file, 0o644) command = f'chown lscpd:lscpd {config_file}' Upgrade.executioner(command, f'Fix config ownership: {config_file}', 0) Upgrade.stdOut(f"Created minimal config: {config_file}", 1) else: Upgrade.stdOut(f"LiteSpeed config exists: {config_file}", 1) # Create PHP socket directory if it doesn't exist php_sock_dir = '/var/run/php/' if not os.path.exists(php_sock_dir): Upgrade.stdOut("Creating PHP socket directory...", 1) os.makedirs(php_sock_dir, exist_ok=True) command = f'chmod 755 {php_sock_dir}' Upgrade.executioner(command, 'Fix PHP socket directory permissions', 0) # Set ownership based on distribution osType = Upgrade.decideDistro() if osType in [Upgrade.centos, Upgrade.cent8, Upgrade.cloudlinux]: command = f'chown apache:apache {php_sock_dir}' else: command = f'chown www-data:www-data {php_sock_dir}' Upgrade.executioner(command, 'Fix PHP socket directory ownership', 0) # Restart LiteSpeed services to apply changes Upgrade.stdOut("Restarting LiteSpeed services...", 1) litespeed_services = ['lsws', 'lscpd'] for service in litespeed_services: command = f'systemctl restart {service}' Upgrade.executioner(command, f'Restart {service}', 0) command = f'systemctl enable {service}' Upgrade.executioner(command, f'Enable {service}', 0) # Verify service is running command = f'systemctl is-active {service}' try: result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.stdout.strip() == 'active': Upgrade.stdOut(f"{service} is running successfully", 1) else: Upgrade.stdOut(f"{service} status: {result.stdout.strip()}", 0) except: Upgrade.stdOut(f"Could not verify {service} status", 0) except Exception as e: Upgrade.stdOut(f"Error fixing LiteSpeed config: {str(e)}", 0) @staticmethod def fixServiceConfiguration(): """Comprehensive service configuration fix for common 503 error causes""" try: Upgrade.stdOut("Applying comprehensive service configuration fixes...", 1) # Upgrade pip first for better package compatibility Upgrade.upgradePip() # Fix PowerDNS configuration Upgrade.fixPowerDNSConfig() # Fix Pure-FTPd configuration Upgrade.fixPureFTPdConfig() # Fix database connectivity Upgrade.fixDatabaseConnectivity() # Fix PHP-FPM services Upgrade.fixPHPFPMServices() # Final service restart and verification Upgrade.restartAndVerifyServices() except Exception as e: Upgrade.stdOut(f"Error in service configuration fix: {str(e)}", 0) @staticmethod def upgradePip(): """Upgrade pip to latest version for better package compatibility""" try: Upgrade.stdOut("Upgrading pip to latest version...", 1) python_path = Upgrade._python_for_manage() if not python_path: Upgrade.stdOut("No Python executable found for pip upgrade", 0) return False # Upgrade pip and essential packages upgrade_command = f"{python_path} -m pip install --upgrade pip setuptools wheel packaging" result = Upgrade.executioner(upgrade_command, "Upgrade pip", 0) if result == 1: Upgrade.stdOut("pip upgraded successfully", 1) return True else: Upgrade.stdOut("WARNING: pip upgrade failed, continuing with current version", 0) return False except Exception as e: Upgrade.stdOut(f"Error upgrading pip: {str(e)}", 0) return False @staticmethod def fixPowerDNSConfig(): """Fix PowerDNS configuration issues""" try: Upgrade.stdOut("Fixing PowerDNS configuration...", 1) # Check if PowerDNS is installed if not os.path.exists('/home/cyberpanel/powerdns'): Upgrade.stdOut("PowerDNS not enabled, skipping...", 1) return # Determine correct service name pdns_service = None result = subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True) if 'pdns.service' in result.stdout: pdns_service = 'pdns' elif 'powerdns.service' in result.stdout: pdns_service = 'powerdns' if not pdns_service: Upgrade.stdOut("PowerDNS service not found", 0) return # Fix PowerDNS configuration files config_files = ['/etc/pdns/pdns.conf', '/etc/powerdns/pdns.conf'] for config_file in config_files: if os.path.exists(config_file): Upgrade.stdOut(f"Configuring PowerDNS: {config_file}", 1) # Read existing content with open(config_file, 'r') as f: content = f.read() # Add missing configuration if not present if 'gmysql-password=' not in content: content += '\ngmysql-password=cyberpanel\n' if 'launch=' not in content: content += 'launch=gmysql\n' # Write back the configuration with open(config_file, 'w') as f: f.write(content) # Set proper permissions os.chmod(config_file, 0o644) command = f'chown root:root {config_file}' Upgrade.executioner(command, f'Fix PowerDNS config ownership', 0) break # Restart PowerDNS service command = f'systemctl restart {pdns_service}' Upgrade.executioner(command, f'Restart PowerDNS', 0) command = f'systemctl enable {pdns_service}' Upgrade.executioner(command, f'Enable PowerDNS', 0) # Verify service is running command = f'systemctl is-active {pdns_service}' result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.stdout.strip() == 'active': Upgrade.stdOut("PowerDNS is running successfully", 1) else: Upgrade.stdOut(f"PowerDNS status: {result.stdout.strip()}", 0) except Exception as e: Upgrade.stdOut(f"Error fixing PowerDNS config: {str(e)}", 0) @staticmethod def fixPureFTPdConfig(): """Fix Pure-FTPd configuration issues""" try: Upgrade.stdOut("Fixing Pure-FTPd configuration...", 1) # Check if Pure-FTPd is installed if not os.path.exists('/home/cyberpanel/pureftpd'): Upgrade.stdOut("Pure-FTPd not enabled, skipping...", 1) return # Determine correct service name ftp_service = None result = subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True) if 'pure-ftpd.service' in result.stdout: ftp_service = 'pure-ftpd' elif 'pureftpd.service' in result.stdout: ftp_service = 'pureftpd' if not ftp_service: Upgrade.stdOut("Pure-FTPd service not found", 0) return # Fix Pure-FTPd configuration files config_files = ['/etc/pure-ftpd/pureftpd-mysql.conf', '/etc/pure-ftpd/db/mysql.conf'] for config_file in config_files: if os.path.exists(config_file): Upgrade.stdOut(f"Configuring Pure-FTPd: {config_file}", 1) # Fix MySQL password configuration command = f"sed -i 's/MYSQLPassword.*/MYSQLPassword cyberpanel/' {config_file}" Upgrade.executioner(command, f'Fix Pure-FTPd MySQL password', 0) # Fix MySQL crypt method for Ubuntu 24.04 compatibility command = f"sed -i 's/MYSQLCrypt md5/MYSQLCrypt crypt/g' {config_file}" Upgrade.executioner(command, f'Fix Pure-FTPd MySQL crypt method', 0) # Set proper permissions os.chmod(config_file, 0o644) command = f'chown root:root {config_file}' Upgrade.executioner(command, f'Fix Pure-FTPd config ownership', 0) # Restart Pure-FTPd service command = f'systemctl restart {ftp_service}' Upgrade.executioner(command, f'Restart Pure-FTPd', 0) command = f'systemctl enable {ftp_service}' Upgrade.executioner(command, f'Enable Pure-FTPd', 0) # Verify service is running command = f'systemctl is-active {ftp_service}' result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.stdout.strip() == 'active': Upgrade.stdOut("Pure-FTPd is running successfully", 1) else: Upgrade.stdOut(f"Pure-FTPd status: {result.stdout.strip()}", 0) except Exception as e: Upgrade.stdOut(f"Error fixing Pure-FTPd config: {str(e)}", 0) @staticmethod def fixDatabaseConnectivity(): """Fix database connectivity issues""" try: Upgrade.stdOut("Fixing database connectivity...", 1) # Determine database service name db_service = None result = subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True) if 'mariadb.service' in result.stdout: db_service = 'mariadb' elif 'mysql.service' in result.stdout: db_service = 'mysql' elif 'mysqld.service' in result.stdout: db_service = 'mysqld' if not db_service: Upgrade.stdOut("Database service not found", 0) return # Ensure database service is running command = f'systemctl restart {db_service}' Upgrade.executioner(command, f'Restart database service', 0) command = f'systemctl enable {db_service}' Upgrade.executioner(command, f'Enable database service', 0) # Wait for database to be ready Upgrade.stdOut("Waiting for database to be ready...", 1) max_attempts = 30 for attempt in range(max_attempts): try: result = subprocess.run(['mysqladmin', 'ping', '-h', 'localhost', '--silent'], capture_output=True) if result.returncode == 0: Upgrade.stdOut("Database is ready", 1) break except: pass time.sleep(2) # Ensure cyberpanel database exists (prefer mariadb CLI; mysql is deprecated) try: _mdb = shutil.which('mariadb') or 'mysql' result = subprocess.run([_mdb, '-e', 'USE cyberpanel;'], capture_output=True) if result.returncode != 0: Upgrade.stdOut("Creating cyberpanel database...", 1) commands = [ _mdb + ' -e "CREATE DATABASE IF NOT EXISTS cyberpanel;"', _mdb + ' -e "CREATE USER IF NOT EXISTS \'cyberpanel\'@\'localhost\' IDENTIFIED BY \'cyberpanel\';"', _mdb + ' -e "GRANT ALL PRIVILEGES ON cyberpanel.* TO \'cyberpanel\'@\'localhost\';"', _mdb + ' -e "FLUSH PRIVILEGES;"' ] for cmd in commands: Upgrade.executioner(cmd, 'Setup cyberpanel database', 0) except: Upgrade.stdOut("Could not verify cyberpanel database", 0) except Exception as e: Upgrade.stdOut(f"Error fixing database connectivity: {str(e)}", 0) @staticmethod def fixPHPFPMServices(): """Fix PHP-FPM services""" try: Upgrade.stdOut("Fixing PHP-FPM services...", 1) # Get available PHP versions php_versions = Upgrade.get_available_php_versions() for version in php_versions: # Determine FPM service name based on distribution osType = Upgrade.decideDistro() if osType in [Upgrade.centos, Upgrade.cent8, Upgrade.cloudlinux]: fpm_service = f'php{version}-php-fpm' else: fpm_service = f'php{version}-fpm' # Check if service exists and restart it result = subprocess.run(['systemctl', 'list-unit-files'], capture_output=True, text=True) if f'{fpm_service}.service' in result.stdout: Upgrade.stdOut(f"Restarting PHP-FPM {version}...", 1) command = f'systemctl restart {fpm_service}' Upgrade.executioner(command, f'Restart PHP-FPM {version}', 0) command = f'systemctl enable {fpm_service}' Upgrade.executioner(command, f'Enable PHP-FPM {version}', 0) # Verify service is running command = f'systemctl is-active {fpm_service}' result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.stdout.strip() == 'active': Upgrade.stdOut(f"PHP-FPM {version} is running", 1) else: Upgrade.stdOut(f"PHP-FPM {version} status: {result.stdout.strip()}", 0) except Exception as e: Upgrade.stdOut(f"Error fixing PHP-FPM services: {str(e)}", 0) @staticmethod def restartAndVerifyServices(): """Restart and verify all critical services""" try: Upgrade.stdOut("Restarting and verifying critical services...", 1) # Reload systemd daemon command = 'systemctl daemon-reload' Upgrade.executioner(command, 'Reload systemd daemon', 0) # Restart critical services in order critical_services = ['lsws', 'lscpd'] all_services_ok = True for service in critical_services: # Check if service exists before trying to manage it check_command = f'systemctl list-unit-files | grep -q "{service}.service"' result = subprocess.run(check_command, shell=True, capture_output=True) if result.returncode != 0: Upgrade.stdOut(f"Service {service} not found, skipping management", 1) continue Upgrade.stdOut(f"Restarting {service}...", 1) command = f'systemctl restart {service}' Upgrade.executioner(command, f'Restart {service}', 0) command = f'systemctl enable {service}' Upgrade.executioner(command, f'Enable {service}', 0) # Verify service is running command = f'systemctl is-active {service}' result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.stdout.strip() == 'active': Upgrade.stdOut(f"✓ {service} is running successfully", 1) else: Upgrade.stdOut(f"✗ {service} is not running (status: {result.stdout.strip()})", 0) all_services_ok = False if all_services_ok: Upgrade.stdOut("All critical services are running successfully!", 1) Upgrade.stdOut("CyberPanel should now be accessible at https://your-server-ip:8090", 1) else: Upgrade.stdOut("Some critical services are not running properly", 0) Upgrade.stdOut("Please check the logs and consider a server restart", 0) except Exception as e: Upgrade.stdOut(f"Error in service verification: {str(e)}", 0) @staticmethod def installPHP73(): try: Upgrade.stdOut("Installing PHP versions based on OS compatibility...", 1) # Get available PHP versions available_versions = Upgrade.get_available_php_versions() if not available_versions: Upgrade.stdOut("No PHP versions available for installation", 0) return Upgrade.stdOut(f"Installing available PHP versions: {', '.join(available_versions)}", 1) for version in available_versions: try: if version in ['74']: # PHP 7.4 only (legacy support) with specific extensions if Upgrade.installedOutput.find(f'lsphp{version}') == -1: extensions = ['json', 'xmlrpc', 'xml', 'tidy', 'soap', 'snmp', 'recode', 'pspell', 'process', 'pgsql', 'pear', 'pdo', 'opcache', 'odbc', 'mysqlnd', 'mcrypt', 'mbstring', 'ldap', 'intl', 'imap', 'gmp', 'gd', 'enchant', 'dba', 'common', 'bcmath'] package_list = f"lsphp{version} " + " ".join([f"lsphp{version}-{ext}" for ext in extensions]) command = f"yum install -y {package_list}" Upgrade.executioner(command, f'Install PHP {version}', 0) elif version in ['80', '81', '82', '83', '84', '85']: # PHP 8.x versions (including 8.5 beta) if Upgrade.installedOutput.find(f'lsphp{version}') == -1: command = f"yum install lsphp{version}* -y" subprocess.call(command, shell=True) Upgrade.stdOut(f"Installed PHP {version}", 1) except Exception as e: Upgrade.stdOut(f"Error installing PHP {version}: {str(e)}", 0) continue except: command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install ' \ 'lsphp7? lsphp7?-common lsphp7?-curl lsphp7?-dev lsphp7?-imap lsphp7?-intl lsphp7?-json ' \ 'lsphp7?-ldap lsphp7?-mysql lsphp7?-opcache lsphp7?-pspell lsphp7?-recode ' \ 'lsphp7?-sqlite3 lsphp7?-tidy' Upgrade.executioner(command, 'Install PHP 7.x', 0) command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp80*' os.system(command) command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp81*' os.system(command) command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp82*' os.system(command) command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp83*' os.system(command) command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp84*' os.system(command) command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp85*' os.system(command) CentOSPath = '/etc/redhat-release' openEulerPath = '/etc/openEuler-release' # if not os.path.exists(CentOSPath) or not os.path.exists(openEulerPath): # command = 'cp /usr/local/lsws/lsphp71/bin/php /usr/bin/' # Upgrade.executioner(command, 'Set default PHP 7.0, 0') @staticmethod def someDirectories(): command = "mkdir -p /usr/local/lscpd/admin/" Upgrade.executioner(command, 0) command = "mkdir -p /usr/local/lscp/cyberpanel/logs" Upgrade.executioner(command, 0) @staticmethod def upgradeDovecot(): try: Upgrade.stdOut("Upgrading Dovecot..") CentOSPath = '/etc/redhat-release' openEulerPath = '/etc/openEuler-release' dovecotConfPath = '/etc/dovecot/' postfixConfPath = '/etc/postfix/' ## Take backup of configurations configbackups = '/home/cyberpanel/configbackups' command = 'mkdir %s' % (configbackups) Upgrade.executioner(command, 0) command = 'cp -pR %s %s' % (dovecotConfPath, configbackups) Upgrade.executioner(command, 0) command = 'cp -pR %s %s' % (postfixConfPath, configbackups) Upgrade.executioner(command, 0) if Upgrade.FindOperatingSytem() == CENTOS8 or Upgrade.FindOperatingSytem() == CENTOS7 or Upgrade.FindOperatingSytem() == openEuler22 or Upgrade.FindOperatingSytem() == openEuler20: command = "yum makecache -y" Upgrade.executioner(command, 0) command = "yum update -y" Upgrade.executioner(command, 0) if Upgrade.FindOperatingSytem() == CENTOS8: command = 'dnf remove dovecot23 dovecot23-mysql -y' Upgrade.executioner(command, 0) command = 'dnf install --enablerepo=gf-plus dovecot23 dovecot23-mysql -y' Upgrade.executioner(command, 0) import django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") django.setup() from mailServer.models import EUsers Upgrade.stdOut("Upgrading passwords...") for items in EUsers.objects.all(): if items.password.find('CRYPT') > -1: continue command = 'doveadm pw -p %s' % (items.password) try: items.password = subprocess.check_output(shlex.split(command)).decode("utf-8").strip('\n') except Exception as e: Upgrade.stdOut(f"Error hashing password for {items.email}: {str(e)}") continue items.save() command = "systemctl restart dovecot" Upgrade.executioner(command, 0) ### Postfix Upgrade command = 'yum remove postfix -y' Upgrade.executioner(command, 0) command = 'yum clean all' Upgrade.executioner(command, 0) if Upgrade.FindOperatingSytem() == CENTOS7: command = 'yum makecache fast' else: command = 'yum makecache -y' Upgrade.executioner(command, 0) if Upgrade.FindOperatingSytem() == CENTOS7: command = 'yum install --enablerepo=gf-plus -y postfix3 postfix3-ldap postfix3-mysql postfix3-pcre' else: command = 'dnf install --enablerepo=gf-plus postfix3 postfix3-mysql -y' Upgrade.executioner(command, 0) ### Restore dovecot/postfix conf command = 'cp -pR %s/dovecot/ /etc/' % (configbackups) Upgrade.executioner(command, 0) command = 'cp -pR %s/postfix/ /etc/' % (configbackups) Upgrade.executioner(command, 0) ## Restored command = 'systemctl restart postfix' Upgrade.executioner(command, 0) elif Upgrade.FindOperatingSytem() == Ubuntu20 or Upgrade.FindOperatingSytem() == Ubuntu22 or Upgrade.FindOperatingSytem() == Ubuntu24 or Upgrade.FindOperatingSytem() == Debian11 or Upgrade.FindOperatingSytem() == Debian12 or Upgrade.FindOperatingSytem() == Debian13: debPath = '/etc/apt/sources.list.d/dovecot.list' # writeToFile = open(debPath, 'w') # writeToFile.write('deb https://repo.dovecot.org/ce-2.3-latest/ubuntu/focal focal main\n') # writeToFile.close() # # command = "apt update -y" # Upgrade.executioner(command, command) # # command = 'dpkg --configure -a' # subprocess.call(command, shell=True) # # command = 'apt --fix-broken install -y' # subprocess.call(command, shell=True) # # command = 'DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade -y' # subprocess.call(command, shell=True) dovecotConf = '/etc/dovecot/dovecot.conf' try: dovecotContent = open(dovecotConf, 'r').read() except Exception as e: Upgrade.stdOut(f"Error reading dovecot config: {str(e)}") dovecotContent = "" if dovecotContent and dovecotContent.find('service stats') == -1: writeToFile = open(dovecotConf, 'a') content = """\nservice stats { unix_listener stats-reader { user = vmail group = vmail mode = 0660 } unix_listener stats-writer { user = vmail group = vmail mode = 0660 } }\n""" writeToFile.write(content) writeToFile.close() # Fix mailbox auto-creation issue if dovecotContent and dovecotContent.find('lda_mailbox_autocreate') == -1: Upgrade.stdOut("Enabling mailbox auto-creation in dovecot...") # Add mailbox auto-creation settings to protocol lda section try: dovecotContent = open(dovecotConf, 'r').read() except Exception as e: Upgrade.stdOut(f"Error reading dovecot config: {str(e)}") dovecotContent = "" if dovecotContent and dovecotContent.find('protocol lda') > -1: # Update existing protocol lda section import re pattern = r'(protocol lda\s*{[^}]*)' replacement = r'\1\n lda_mailbox_autocreate = yes\n lda_mailbox_autosubscribe = yes' if isinstance(dovecotContent, str): dovecotContent = re.sub(pattern, replacement, dovecotContent) writeToFile = open(dovecotConf, 'w') writeToFile.write(dovecotContent) writeToFile.close() else: # Add new protocol lda section writeToFile = open(dovecotConf, 'a') content = """\nprotocol lda { lda_mailbox_autocreate = yes lda_mailbox_autosubscribe = yes }\n""" writeToFile.write(content) writeToFile.close() command = 'systemctl restart dovecot' Upgrade.executioner(command, command, 0) command = 'rm -rf %s' % (configbackups) Upgrade.executioner(command, command, 0) Upgrade.stdOut("Dovecot upgraded.") except Exception as e: ErrorSanitizer.log_error_securely(e, 'upgradeDovecot') Upgrade.stdOut("Failed to upgrade Dovecot [upgradeDovecot]") @staticmethod def installRestic(): CentOSPath = '/etc/redhat-release' openEulerPath = '/etc/openEuler-release' if os.path.exists(CentOSPath) or os.path.exists(openEulerPath): if Upgrade.installedOutput.find('restic') == -1: command = 'yum install restic -y' Upgrade.executioner(command, 'Install Restic') command = 'restic self-update' Upgrade.executioner(command, 'Install Restic') else: if Upgrade.installedOutput.find('restic/bionic,now 0.8') == -1: command = 'apt-get update -y' Upgrade.executioner(command, 'Install Restic') command = 'apt-get install restic -y' Upgrade.executioner(command, 'Install Restic') command = 'restic self-update' Upgrade.executioner(command, 'Install Restic') @staticmethod def UpdateMaxSSLCons(): command = "sed -i 's|2000|10000|g' /usr/local/lsws/conf/httpd_config.xml" Upgrade.executioner(command, 0) command = "sed -i 's|200|10000|g' /usr/local/lsws/conf/httpd_config.xml" Upgrade.executioner(command, 0) @staticmethod def installCLScripts(): try: CentOSPath = '/etc/redhat-release' openEulerPath = '/etc/openEuler-release' if os.path.exists(CentOSPath) or os.path.exists(openEulerPath): command = 'mkdir -p /opt/cpvendor/etc/' Upgrade.executioner(command, 0) content = """[integration_scripts] panel_info = /usr/local/CyberCP/CLScript/panel_info.py packages = /usr/local/CyberCP/CLScript/CloudLinuxPackages.py users = /usr/local/CyberCP/CLScript/CloudLinuxUsers.py domains = /usr/local/CyberCP/CLScript/CloudLinuxDomains.py resellers = /usr/local/CyberCP/CLScript/CloudLinuxResellers.py admins = /usr/local/CyberCP/CLScript/CloudLinuxAdmins.py db_info = /usr/local/CyberCP/CLScript/CloudLinuxDB.py [lvemanager_config] ui_user_info = /usr/local/CyberCP/CLScript/UserInfo.py base_path = /usr/local/lvemanager run_service = 1 service_port = 9000 """ if not os.path.exists('/opt/cpvendor/etc/integration.ini'): writeToFile = open('/opt/cpvendor/etc/integration.ini', 'w') writeToFile.write(content) writeToFile.close() command = 'mkdir -p /etc/cagefs/exclude' Upgrade.executioner(command, command, 0) content = """cyberpanel docker ftpuser lscpd opendkim pdns vmail """ writeToFile = open('/etc/cagefs/exclude/cyberpanelexclude', 'w') writeToFile.write(content) writeToFile.close() except: pass @staticmethod def runSomeImportantBash(): # Remove invalid crons from /etc/crontab Reference: https://github.com/usmannasir/cyberpanel/issues/216 command = """sed -i '/CyberCP/d' /etc/crontab""" Upgrade.executioner(command, command, 0, True) # Ensure log directory exists for scheduled scans if not os.path.exists('/usr/local/lscp/logs'): try: os.makedirs('/usr/local/lscp/logs', mode=0o755) except: pass if os.path.exists('/usr/local/lsws/conf/httpd.conf'): # Setup /usr/local/lsws/conf/httpd.conf to use new Logformat standard for better stats and accesslogs command = """sed -i "s|^LogFormat.*|LogFormat '%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"' combined|g" /usr/local/lsws/conf/httpd.conf""" Upgrade.executioner(command, command, 0, True) # Fix all existing vhost confs to use new Logformat standard for better stats and accesslogs command = """find /usr/local/lsws/conf/vhosts/ -type f -name 'vhost.conf' -exec sed -i "s/.*CustomLog.*/ LogFormat '%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"' combined\n&/g" {} \;""" Upgrade.executioner(command, command, 0, True) # Install any Cyberpanel missing crons to root crontab so its visible to users via crontab -l as root user # Install findBWUsage cron if missing CentOSPath = '/etc/redhat-release' openEulerPath = '/etc/openEuler-release' if os.path.exists(CentOSPath) or os.path.exists(openEulerPath): cronPath = '/var/spool/cron/root' else: cronPath = '/var/spool/cron/crontabs/root' if os.path.exists(cronPath): data = open(cronPath, 'r').read() if data.find('findBWUsage') == -1: # Randomize acme.sh and renew.py cron schedules to avoid traffic spikes to Let's Encrypt # Each installation gets a random day (0-6 Sun-Sat), hour, and minute to spread load acme_hour = random.randint(0, 23) acme_minute = random.randint(0, 59) renew_weekday = random.randint(0, 6) # 0=Sun, 1=Mon, ..., 6=Sat renew_hour = random.randint(0, 23) renew_minute = random.randint(0, 59) content = """ 0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/findBWUsage.py >/dev/null 2>&1 0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py hourlyCleanup >/dev/null 2>&1 0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup >/dev/null 2>&1 0 2 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/upgradeCritical.py >/dev/null 2>&1 %d %d * * %d /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/renew.py >/dev/null 2>&1 %d %d * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null 0 1 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/manage.py ssl_reconcile --all >/dev/null 2>&1 */3 * * * * if ! find /home/*/public_html/ -maxdepth 2 -type f -newer /usr/local/lsws/cgid -name '.htaccess' -exec false {} +; then /usr/local/lsws/bin/lswsctrl restart; fi * * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/manage.py run_scheduled_scans >/usr/local/lscp/logs/scheduled_scans.log 2>&1 """ writeToFile = open(cronPath, 'w') writeToFile.write(content % (renew_minute, renew_hour, renew_weekday, acme_minute, acme_hour)) writeToFile.close() if data.find('IncScheduler.py') == -1: content = """ 0 12 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Daily 0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Weekly """ writeToFile = open(cronPath, 'a') writeToFile.write(content) writeToFile.close() if data.find("IncScheduler.py '30 Minutes'") == -1: content = """ */30 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '30 Minutes' 0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '1 Hour' 0 */6 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '6 Hours' 0 */12 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '12 Hours' 0 1 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '1 Day' 0 0 */3 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '3 Days' 0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '1 Week' """ writeToFile = open(cronPath, 'a') writeToFile.write(content) writeToFile.close() # Add AI Scanner scheduled scans cron job if missing if data.find('run_scheduled_scans') == -1: content = """ * * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/manage.py run_scheduled_scans >/usr/local/lscp/logs/scheduled_scans.log 2>&1 """ writeToFile = open(cronPath, 'a') writeToFile.write(content) writeToFile.close() else: # Randomize acme.sh and renew.py cron schedules to avoid traffic spikes to Let's Encrypt # Each installation gets a random day (0-6 Sun-Sat), hour, and minute to spread load acme_hour = random.randint(0, 23) acme_minute = random.randint(0, 59) renew_weekday = random.randint(0, 6) # 0=Sun, 1=Mon, ..., 6=Sat renew_hour = random.randint(0, 23) renew_minute = random.randint(0, 59) content = """ 0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/findBWUsage.py >/dev/null 2>&1 0 * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py hourlyCleanup >/dev/null 2>&1 0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup >/dev/null 2>&1 0 2 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/upgradeCritical.py >/dev/null 2>&1 %d %d * * %d /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/renew.py >/dev/null 2>&1 %d %d * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null 0 1 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/manage.py ssl_reconcile --all >/dev/null 2>&1 0 0 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Daily 0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Weekly * * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/manage.py run_scheduled_scans >/usr/local/lscp/logs/scheduled_scans.log 2>&1 """ % (renew_minute, renew_hour, renew_weekday, acme_minute, acme_hour) writeToFile = open(cronPath, 'w') writeToFile.write(content) writeToFile.close() ### Check and remove OLS restart if lsws ent detected if not os.path.exists('/usr/local/lsws/bin/openlitespeed'): data = open(cronPath, 'r').readlines() writeToFile = open(cronPath, 'w') for items in data: if items.find('-maxdepth 2 -type f -newer') > -1: pass else: writeToFile.writelines(items) writeToFile.close() if not os.path.exists(CentOSPath) or not os.path.exists(openEulerPath): command = 'chmod 600 %s' % (cronPath) Upgrade.executioner(command, 0) @staticmethod def UpdateConfigOfCustomACL(): sys.path.append('/usr/local/CyberCP') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") import django django.setup() from loginSystem.models import ACL for acl in ACL.objects.all(): if acl.name == 'admin' or acl.name == 'reseller' or acl.name == 'user': continue elif acl.config == '{}': acl.config = '{"adminStatus":%s, "versionManagement": %s, "createNewUser": %s, "listUsers": %s, "deleteUser": %s, "resellerCenter": %s, "changeUserACL": %s, "createWebsite": %s, "modifyWebsite": %s, "suspendWebsite": %s, "deleteWebsite": %s, "createPackage": %s, "listPackages": %s, "deletePackage": %s, "modifyPackage": %s, "createDatabase": %s, "deleteDatabase": %s, "listDatabases": %s, "createNameServer": %s, "createDNSZone": %s, "deleteZone": %s, "addDeleteRecords": %s, "createEmail": %s, "listEmails": %s, "deleteEmail": %s, "emailForwarding": %s, "changeEmailPassword": %s, "dkimManager": %s, "createFTPAccount": %s, "deleteFTPAccount": %s, "listFTPAccounts": %s, "createBackup": %s, "restoreBackup": %s, "addDeleteDestinations": %s, "scheduleBackups": %s, "remoteBackups": %s, "googleDriveBackups": %s, "manageSSL": %s, "hostnameSSL": %s, "mailServerSSL": %s }' \ % (str(acl.adminStatus), str(acl.versionManagement), str(acl.createNewUser), str(acl.listUsers), str(acl.deleteUser), str(acl.resellerCenter), str(acl.changeUserACL), str(acl.createWebsite), str(acl.modifyWebsite), str(acl.suspendWebsite), str(acl.deleteWebsite), str(acl.createPackage), str(acl.listPackages), str(acl.deletePackage), str(acl.modifyPackage), str(acl.createDatabase), str(acl.deleteDatabase), str(acl.listDatabases), str(acl.createNameServer), str(acl.createDNSZone), str(acl.deleteZone), str(acl.addDeleteRecords), str(acl.createEmail), str(acl.listEmails), str(acl.deleteEmail), str(acl.emailForwarding), str(acl.changeEmailPassword), str(acl.dkimManager), str(acl.createFTPAccount), str(acl.deleteFTPAccount), str(acl.listFTPAccounts), str(acl.createBackup), str(acl.restoreBackup), str(acl.addDeleteDestinations), str(acl.scheduleBackups), str(acl.remoteBackups), '1', str(acl.manageSSL), str(acl.hostnameSSL), str(acl.mailServerSSL)) acl.save() @staticmethod def CreateMissingPoolsforFPM(): """ Create missing PHP-FPM pool configurations for all PHP versions. This function ensures all PHP versions have proper pool configurations to prevent ImunifyAV/Imunify360 installation failures. """ try: # Detect OS and set paths CentOSPath = '/etc/redhat-release' if os.path.exists(CentOSPath): # CentOS/RHEL/CloudLinux paths serverRootPath = '/etc/httpd' configBasePath = '/etc/httpd/conf.d/' sockPath = '/var/run/php-fpm/' runAsUser = 'apache' group = 'nobody' # Define PHP pool paths for CentOS php_paths = { '5.4': '/opt/remi/php54/root/etc/php-fpm.d/', '5.5': '/opt/remi/php55/root/etc/php-fpm.d/', '5.6': '/etc/opt/remi/php56/php-fpm.d/', '7.0': '/etc/opt/remi/php70/php-fpm.d/', '7.1': '/etc/opt/remi/php71/php-fpm.d/', '7.2': '/etc/opt/remi/php72/php-fpm.d/', '7.3': '/etc/opt/remi/php73/php-fpm.d/', '7.4': '/etc/opt/remi/php74/php-fpm.d/', '8.0': '/etc/opt/remi/php80/php-fpm.d/', '8.1': '/etc/opt/remi/php81/php-fpm.d/', '8.2': '/etc/opt/remi/php82/php-fpm.d/', '8.3': '/etc/opt/remi/php83/php-fpm.d/', '8.4': '/etc/opt/remi/php84/php-fpm.d/', '8.5': '/etc/opt/remi/php85/php-fpm.d/' } else: # Ubuntu/Debian paths serverRootPath = '/etc/apache2' configBasePath = '/etc/apache2/sites-enabled/' sockPath = '/var/run/php/' runAsUser = 'www-data' group = 'nogroup' # Define PHP pool paths for Ubuntu php_paths = { '5.4': '/etc/php/5.4/fpm/pool.d/', '5.5': '/etc/php/5.5/fpm/pool.d/', '5.6': '/etc/php/5.6/fpm/pool.d/', '7.0': '/etc/php/7.0/fpm/pool.d/', '7.1': '/etc/php/7.1/fpm/pool.d/', '7.2': '/etc/php/7.2/fpm/pool.d/', '7.3': '/etc/php/7.3/fpm/pool.d/', '7.4': '/etc/php/7.4/fpm/pool.d/', '8.0': '/etc/php/8.0/fpm/pool.d/', '8.1': '/etc/php/8.1/fpm/pool.d/', '8.2': '/etc/php/8.2/fpm/pool.d/', '8.3': '/etc/php/8.3/fpm/pool.d/', '8.4': '/etc/php/8.4/fpm/pool.d/', '8.5': '/etc/php/8.5/fpm/pool.d/' } # Check if server root exists if not os.path.exists(serverRootPath): logging.CyberCPLogFileWriter.writeToFile(f'Server root path not found: {serverRootPath}') return 1 # Create pool configurations for all PHP versions for version, pool_path in php_paths.items(): if os.path.exists(pool_path): www_conf = os.path.join(pool_path, 'www.conf') # Skip if www.conf already exists if os.path.exists(www_conf): logging.CyberCPLogFileWriter.writeToFile(f'PHP {version} pool config already exists: {www_conf}') continue # Create the pool configuration pool_name = f'php{version.replace(".", "")}default' sock_name = f'php{version}-fpm.sock' content = f'''[{pool_name}] user = {runAsUser} group = {runAsUser} listen = {sockPath}{sock_name} listen.owner = {runAsUser} listen.group = {group} listen.mode = 0660 pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 pm.max_requests = 1000 pm.status_path = /status ping.path = /ping ping.response = pong request_terminate_timeout = 300 request_slowlog_timeout = 10 slowlog = /var/log/php{version}-fpm-slow.log ''' try: # Write the configuration file with open(www_conf, 'w') as f: f.write(content) # Set proper permissions os.chown(www_conf, 0, 0) # root:root os.chmod(www_conf, 0o644) logging.CyberCPLogFileWriter.writeToFile(f'Created PHP {version} pool config: {www_conf}') except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error creating PHP {version} pool config: {str(e)}') else: logging.CyberCPLogFileWriter.writeToFile(f'PHP {version} pool directory not found: {pool_path}') # Restart PHP-FPM services to apply configurations Upgrade.restartPHPFPMServices() return 0 except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error in CreateMissingPoolsforFPM: {str(e)}') return 1 @staticmethod def restartPHPFPMServices(): """ Restart all PHP-FPM services to apply new pool configurations. This ensures that ImunifyAV/Imunify360 installation will work properly. """ try: # Define all possible PHP versions php_versions = ['5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] restarted_count = 0 total_count = 0 for version in php_versions: service_name = f'php{version}-fpm' # Check if service exists try: result = subprocess.run(['systemctl', 'list-unit-files', service_name], capture_output=True, text=True, timeout=10) if result.returncode == 0 and service_name in result.stdout: total_count += 1 # Restart the service restart_result = subprocess.run(['systemctl', 'restart', service_name], capture_output=True, text=True, timeout=30) if restart_result.returncode == 0: # Check if service is actually running status_result = subprocess.run(['systemctl', 'is-active', service_name], capture_output=True, text=True, timeout=10) if status_result.returncode == 0 and 'active' in status_result.stdout: restarted_count += 1 logging.CyberCPLogFileWriter.writeToFile(f'Successfully restarted {service_name}') else: logging.CyberCPLogFileWriter.writeToFile(f'Warning: {service_name} restarted but not active') else: logging.CyberCPLogFileWriter.writeToFile(f'Failed to restart {service_name}: {restart_result.stderr}') except subprocess.TimeoutExpired: logging.CyberCPLogFileWriter.writeToFile(f'Timeout restarting {service_name}') except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error restarting {service_name}: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'PHP-FPM restart summary: {restarted_count}/{total_count} services restarted successfully') return restarted_count, total_count except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error in restartPHPFPMServices: {str(e)}') return 0, 0 @staticmethod def setupPHPSymlink(): try: # Try to find available PHP version (prioritize modern stable versions) # Priority: 8.3 (recommended), 8.2, 8.4, 8.5, 8.1, 8.0, then older versions php_versions = ['83', '82', '84', '85', '81', '80', '74', '73', '72', '71'] selected_php = None for version in php_versions: if os.path.exists(f'/usr/local/lsws/lsphp{version}/bin/php'): selected_php = version Upgrade.stdOut(f"Found PHP {version}, using as default", 1) break if not selected_php: # Try to install PHP 8.3 as fallback (modern stable version) Upgrade.stdOut("No PHP found, installing PHP 8.3 as fallback...") # Install PHP 8.3 based on OS if os.path.exists(Upgrade.CentOSPath) or os.path.exists(Upgrade.openEulerPath): command = 'yum install lsphp83 lsphp83-* -y' Upgrade.executioner(command, 'Install PHP 8.3', 0) else: command = 'DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install lsphp83 lsphp83-*' Upgrade.executioner(command, 'Install PHP 8.3', 0) # Verify installation if not os.path.exists('/usr/local/lsws/lsphp83/bin/php'): Upgrade.stdOut('[ERROR] Failed to install PHP 8.3') return 0 selected_php = '83' # Remove existing PHP symlink if it exists if os.path.exists('/usr/bin/php'): os.remove('/usr/bin/php') # Create symlink to selected PHP version command = f'ln -s /usr/local/lsws/lsphp{selected_php}/bin/php /usr/bin/php' Upgrade.executioner(command, f'Setup PHP Symlink to {selected_php}', 0) Upgrade.stdOut(f"PHP symlink updated to PHP {selected_php} successfully.") except Exception as e: ErrorSanitizer.log_error_securely(e, 'setupPHPSymlink') Upgrade.stdOut('[ERROR] Failed to setup PHP symlink [setupPHPSymlink]') return 0 return 1 @staticmethod def upgrade(branch): if branch.find('SoftUpgrade') > -1: Upgrade.SoftUpgrade = 1 branch = branch.split(',')[1] # Upgrade.stdOut("Upgrades are currently disabled") # return 0 if os.path.exists(Upgrade.CentOSPath) or os.path.exists(Upgrade.openEulerPath): command = 'yum list installed' try: Upgrade.installedOutput = subprocess.check_output(shlex.split(command)).decode() except Exception as e: Upgrade.stdOut(f"Error getting installed packages: {str(e)}") Upgrade.installedOutput = "" else: command = 'apt list' try: Upgrade.installedOutput = subprocess.check_output(shlex.split(command)).decode() except Exception as e: Upgrade.stdOut(f"Error getting installed packages: {str(e)}") Upgrade.installedOutput = "" # command = 'systemctl stop cpssh' # Upgrade.executioner(command, 'fix csf if there', 0) ## Add LSPHP7.4 TO LSWS Ent configs if not os.path.exists('/usr/local/lsws/bin/openlitespeed'): if os.path.exists('httpd_config.xml'): os.remove('httpd_config.xml') command = 'wget https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/install/litespeed/httpd_config.xml' Upgrade.executioner(command, command, 0) # os.remove('/usr/local/lsws/conf/httpd_config.xml') # shutil.copy('httpd_config.xml', '/usr/local/lsws/conf/httpd_config.xml') Upgrade.updateRepoURL() os.chdir("/usr/local") if os.path.exists(Upgrade.CentOSPath) or os.path.exists(Upgrade.openEulerPath): command = 'yum remove yum-plugin-priorities -y' Upgrade.executioner(command, 'remove yum-plugin-priorities', 0) ## Current Version ### if this is a soft upgrade from front end do not stop lscpd, as lscpd is controlling the front end if Upgrade.SoftUpgrade == 0: command = "systemctl stop lscpd" Upgrade.executioner(command, 'stop lscpd', 0) Upgrade.fixSudoers() # Upgrade.mountTemp() ### fix a temp issue causing upgrade problem fstab = "/etc/fstab" if open(fstab, 'r').read().find('/usr/.tempdisk')>-1: command = 'umount -l /tmp' Upgrade.executioner(command, 'tmp adjustment', 0) command = 'mount -t tmpfs -o size=2G tmpfs /tmp' Upgrade.executioner(command, 'tmp adjustment', 0) Upgrade.dockerUsers() Upgrade.setupPHPSymlink() Upgrade.setupComposer() # OpenLiteSpeed: ensure 1.8.5+ (add LiteSpeed repo, upgrade package); only overlay custom binary if still < 1.8.5 if os.path.exists('/usr/local/lsws/bin/openlitespeed'): Upgrade.add_litespeed_repo() if os.path.exists(Upgrade.CentOSPath) or os.path.exists(Upgrade.openEulerPath): Upgrade.executioner('dnf install -y openlitespeed || yum install -y openlitespeed', 'Upgrade OpenLiteSpeed package', 0) else: Upgrade.executioner('DEBIAN_FRONTEND=noninteractive apt-get -y install --only-upgrade openlitespeed 2>/dev/null || DEBIAN_FRONTEND=noninteractive apt-get -y install openlitespeed', 'Upgrade OpenLiteSpeed package', 0, shell=True) ols_ver = Upgrade.get_installed_ols_version() if ols_ver and ols_ver >= (1, 8, 5): Upgrade.stdOut("OpenLiteSpeed 1.8.5+ detected; keeping official binary (no custom overlay).") else: Upgrade.installCustomOLSBinaries() ## versionNumbring = Upgrade.downloadLink() if os.path.exists('/usr/local/CyberPanel.' + versionNumbring): os.remove('/usr/local/CyberPanel.' + versionNumbring) ## # execPath = "sudo /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/csf.py" # execPath = execPath + " removeCSF" # Upgrade.executioner(execPath, 'fix csf if there', 0) Upgrade.downloadAndUpgrade(versionNumbring, branch) versionNumbring = Upgrade.downloadLink() Upgrade.download_install_phpmyadmin() Upgrade.downoad_and_install_raindloop() ## ## Upgrade.mailServerMigrations() Upgrade.emailMarketingMigrationsa() Upgrade.dockerMigrations() Upgrade.CLMigrations() Upgrade.IncBackupMigrations() Upgrade.installRestic() ## # Upgrade.setupVirtualEnv() ## Upgrade.applyLoginSystemMigrations() Upgrade.homeDirectoryMigrations() ## Put function here to update custom ACLs Upgrade.UpdateConfigOfCustomACL() Upgrade.s3BackupMigrations() Upgrade.containerMigrations() Upgrade.manageServiceMigrations() Upgrade.enableServices() # Apply AlmaLinux 9 fixes before other installations Upgrade.fix_almalinux9_mariadb() Upgrade.installPHP73() Upgrade.setupCLI() Upgrade.someDirectories() Upgrade.installLSCPD(branch) Upgrade.FixCurrentQuoatasSystem() ## Fix Apache configuration issues after upgrade Upgrade.fixApacheConfiguration() # Fix LiteSpeed configuration files if missing Upgrade.fixLiteSpeedConfig() # Fix comprehensive service configuration issues (503 error prevention) Upgrade.fixServiceConfiguration() # Fix subdomain log configurations Upgrade.fixSubdomainLogConfigurations() ### General migrations are not needed any more # Upgrade.GeneralMigrations() # Fix baseTemplate migrations for AlmaLinux 9 and Ubuntu 24 compatibility Upgrade.fixBaseTemplateMigrations() # Upgrade.p3() ## Also disable email service upgrade # if os.path.exists(postfixPath): # Upgrade.upgradeDovecot() ## Upgrade version Upgrade.fixPermissions() ## ### Disable version upgrade too # Upgrade.upgradeVersion() Upgrade.UpdateMaxSSLCons() ## Update LSCPD PHP phpPath = '/usr/local/lscp/fcgi-bin/lsphp' try: os.remove(phpPath) except: pass # Try to find available PHP binary in order of preference (modern stable first) php_versions = ['83', '82', '84', '85', '81', '80', '74', '73', '72', '71'] php_binary_found = False for version in php_versions: php_binary = f'/usr/local/lsws/lsphp{version}/bin/lsphp' if os.path.exists(php_binary): command = f'cp {php_binary} {phpPath}' Upgrade.executioner(command, 0) Upgrade.stdOut(f"Using PHP {version} for LSCPD", 1) php_binary_found = True break if not php_binary_found: Upgrade.stdOut("Warning: No PHP binary found for LSCPD", 0) # Try to create a symlink to any available PHP try: command = 'find /usr/local/lsws -name "lsphp" -type f 2>/dev/null | head -1' result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.stdout.strip(): php_binary = result.stdout.strip() command = f'cp {php_binary} {phpPath}' Upgrade.executioner(command, 0) Upgrade.stdOut(f"Using found PHP binary: {php_binary}", 1) except: pass if Upgrade.SoftUpgrade == 0: try: command = "systemctl start lscpd" Upgrade.executioner(command, 'Start LSCPD', 0) except: pass # Try to start other services if they exist # Enhanced service startup with AlmaLinux 9 support services_to_start = ['fastapi_ssh_server', 'cyberpanel'] # Special handling for AlmaLinux 9 MariaDB service if Upgrade.is_almalinux9(): Upgrade.stdOut("AlmaLinux 9 detected - applying enhanced service management", 1) mariadb_services = ['mariadb', 'mysql', 'mysqld'] for service in mariadb_services: try: check_command = f"systemctl list-unit-files | grep -q {service}" result = subprocess.run(check_command, shell=True, capture_output=True) if result.returncode == 0: command = f"systemctl restart {service}" Upgrade.executioner(command, f'Restart {service} for AlmaLinux 9', 0) command = f"systemctl enable {service}" Upgrade.executioner(command, f'Enable {service} for AlmaLinux 9', 0) Upgrade.stdOut(f"MariaDB service managed as {service} on AlmaLinux 9", 1) break except Exception as e: Upgrade.stdOut(f"Could not manage MariaDB service {service}: {str(e)}", 0) continue for service in services_to_start: try: # Check if service exists check_command = f"systemctl list-unit-files | grep -q {service}" result = subprocess.run(check_command, shell=True, capture_output=True) if result.returncode == 0: command = f"systemctl start {service}" Upgrade.executioner(command, f'Start {service}', 0) else: Upgrade.stdOut(f"Service {service} not found, skipping", 0) except Exception as e: Upgrade.stdOut(f"Could not start {service}: {str(e)}", 0) # Remove CSF if installed and restore firewalld (CSF is being discontinued on August 31, 2025) if os.path.exists('/etc/csf'): print("CSF detected - removing CSF and restoring firewalld...") print("Note: ConfigServer Firewall (CSF) is being discontinued on August 31, 2025") # Remove CSF and restore firewalld execPath = "sudo /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/csf.py" execPath = execPath + " removeCSF" Upgrade.executioner(execPath, 'Remove CSF and restore firewalld', 0) print("CSF has been removed and firewalld has been restored.") # Remove configservercsf directory if it exists if os.path.exists('/usr/local/CyberCP/configservercsf'): command = 'rm -rf /usr/local/CyberCP/configservercsf' Upgrade.executioner(command, 'Remove configservercsf directory', 1) command = 'systemctl stop cpssh' Upgrade.executioner(command, 'fix csf if there', 0) Upgrade.AutoUpgradeAcme() Upgrade.installCLScripts() Upgrade.runSomeImportantBash() Upgrade.FixRSPAMDConfig() Upgrade.CreateMissingPoolsforFPM() # ## Handle ImunifyAV and Imunify360 separately # Both products use the same config file, so we need to read its content to determine which product integrationConfig = '/etc/sysconfig/imunify360/integration.conf' if os.path.exists(integrationConfig): try: with open(integrationConfig, 'r') as f: configContent = f.read() # Check which product the config file is for by looking at the ui_path if 'ui_path =/usr/local/CyberCP/public/imunifyav' in configContent: # This is ImunifyAV configuration Upgrade.stdOut("Detected ImunifyAV configuration, reconfiguring...") imunifyAVPath = '/usr/local/CyberCP/public/imunifyav' if os.path.exists(imunifyAVPath): execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/CLManager/CageFS.py" command = execPath + " --function submitinstallImunifyAV" Upgrade.executioner(command, command, 1) # Set permissions on ImunifyAV execute file imunifyAVExecute = '/usr/local/CyberCP/public/imunifyav/bin/execute.py' if os.path.exists(imunifyAVExecute): command = 'chmod +x ' + imunifyAVExecute Upgrade.executioner(command, command, 1) Upgrade.stdOut("ImunifyAV execute permissions set") else: Upgrade.stdOut("ImunifyAV execute.py file not found") else: Upgrade.stdOut("ImunifyAV directory not found despite config file existing") elif 'ui_path =/usr/local/CyberCP/public/imunify' in configContent: # This is Imunify360 configuration Upgrade.stdOut("Detected Imunify360 configuration, checking system installation...") imunify360Path = '/usr/local/CyberCP/public/imunify' if os.path.exists(imunify360Path): # Check if Imunify360 is actually installed on the system imunify360Installed = False if os.path.exists('/usr/bin/imunify360-agent') or os.path.exists('/opt/imunify360'): imunify360Installed = True Upgrade.stdOut("Imunify360 system installation detected") if imunify360Installed: Upgrade.stdOut("Imunify360 directory found and system is installed, ensuring proper integration...") # Reinstall Imunify360 firewall to ensure integration command = "yum reinstall imunify360-firewall-generic -y" if os.path.exists(Upgrade.CentOSPath) else "apt install --reinstall imunify360-firewall-generic -y" Upgrade.executioner(command, command, 1) else: Upgrade.stdOut("Imunify360 directory found but system not installed - manual installation may be needed") # Set permissions on Imunify360 execute file imunify360Execute = '/usr/local/CyberCP/public/imunify/bin/execute.py' if os.path.exists(imunify360Execute): command = f'chmod +x {imunify360Execute}' Upgrade.executioner(command, f'Setting execute permissions on Imunify360 file', 0) Upgrade.stdOut("Imunify360 execute permissions set") else: Upgrade.stdOut("Imunify360 execute.py file not found") else: Upgrade.stdOut("Imunify360 directory not found despite config file existing") else: Upgrade.stdOut(f"Unknown product in integration config file. Config content: {configContent[:200]}...") except Exception as e: Upgrade.stdOut(f"Error reading integration config file: {str(e)}") else: Upgrade.stdOut("No Imunify integration config file found") @staticmethod def restoreImunify360(): """Restore and reconfigure Imunify360 after upgrade""" try: Upgrade.stdOut("=== STARTING IMUNIFY360 RESTORATION ===") Upgrade.stdOut("Checking for Imunify360 restoration...") # Check if Imunify360 directories were restored imunifyPath = '/usr/local/CyberCP/public/imunify' imunifyAVPath = '/usr/local/CyberCP/public/imunifyav' configPath = '/etc/sysconfig/imunify360/integration.conf' Upgrade.stdOut(f"Checking if Imunify360 path exists: {imunifyPath}") Upgrade.stdOut(f"Path exists: {os.path.exists(imunifyPath)}") restored = False # Handle main Imunify360 firewall if os.path.exists(imunifyPath): Upgrade.stdOut("Imunify360 directory found, checking if reinstallation is needed...") # Check if Imunify360 is actually installed on the system if os.path.exists('/usr/bin/imunify360-agent') or os.path.exists('/opt/imunify360'): Upgrade.stdOut("Imunify360 appears to be installed on system, ensuring proper integration...") # Reinstall to ensure proper integration command = "yum reinstall imunify360-firewall-generic -y" if os.path.exists(Upgrade.CentOSPath) else "apt install --reinstall imunify360-firewall-generic -y" if Upgrade.executioner(command, command, 1): Upgrade.stdOut("Imunify360 firewall reinstalled successfully") restored = True else: Upgrade.stdOut("Warning: Failed to reinstall Imunify360 firewall") else: Upgrade.stdOut("Imunify360 not found on system, skipping firewall reinstallation") # Handle ImunifyAV if os.path.exists(imunifyAVPath): Upgrade.stdOut("ImunifyAV directory found, reconfiguring...") if os.path.exists(configPath): execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/CLManager/CageFS.py" command = execPath + " --function submitinstallImunifyAV" if Upgrade.executioner(command, command, 1): Upgrade.stdOut("ImunifyAV reconfigured successfully") restored = True # Ensure execute permissions executePath = '/usr/local/CyberCP/public/imunifyav/bin/execute.py' if os.path.exists(executePath): command = f'chmod +x {executePath}' Upgrade.executioner(command, command, 1) # Handle main Imunify execute permissions - comprehensive solution for missing files if os.path.exists(imunifyPath): # First, check if the bin directory and execute.py file exist binDir = '/usr/local/CyberCP/public/imunify/bin' executeFile = '/usr/local/CyberCP/public/imunify/bin/execute.py' if not os.path.exists(binDir): Upgrade.stdOut(f"Warning: Imunify360 bin directory missing at {binDir}") # Try to find if execute.py exists elsewhere findCommand = f'find {imunifyPath} -name "execute.py" -type f 2>/dev/null' Upgrade.stdOut(f"Searching for execute.py files with command: {findCommand}") findResult = subprocess.getstatusoutput(findCommand) Upgrade.stdOut(f"Find command result: exit_code={findResult[0]}, output='{findResult[1]}'") if findResult[0] == 0 and findResult[1].strip(): Upgrade.stdOut(f"Found execute.py files: {findResult[1]}") # Set permissions on all found execute.py files command = f'find {imunifyPath} -name "execute.py" -type f -exec chmod +x {{}} \\; 2>/dev/null || true' Upgrade.executioner(command, 'Setting execute permissions on found execute.py files', 0) else: Upgrade.stdOut("No execute.py files found in Imunify360 directory - installation may be incomplete") else: # Bin directory exists, try the direct approach Upgrade.stdOut(f"Bin directory exists at {binDir}, attempting to set execute permissions") Upgrade.stdOut(f"Checking if execute.py exists: {executeFile}") Upgrade.stdOut(f"File exists: {os.path.exists(executeFile)}") # Try direct chmod command first if os.path.exists(executeFile): Upgrade.stdOut("File exists, trying direct chmod command") command = f'chmod +x {executeFile}' Upgrade.stdOut(f"Executing direct command: {command}") directResult = Upgrade.executioner(command, f'Direct chmod on {executeFile}', 0) Upgrade.stdOut(f"Direct command result: {directResult}") if directResult: Upgrade.stdOut("SUCCESS: Direct chmod worked!") restored = True else: Upgrade.stdOut("FAILED: Direct chmod failed, trying alternative") # Try the community method as fallback command = f'cd {imunifyPath} && chmod +x ./bin/execute.py 2>/dev/null || true' Upgrade.stdOut(f"Trying community method: {command}") communityResult = Upgrade.executioner(command, 'Community method chmod', 0) Upgrade.stdOut(f"Community method result: {communityResult}") if communityResult: Upgrade.stdOut("SUCCESS: Community method worked!") restored = True else: Upgrade.stdOut("FAILED: Both methods failed") else: Upgrade.stdOut(f"ERROR: execute.py file not found at {executeFile}") # Try find method as final fallback Upgrade.stdOut("Trying find method as final fallback") command = f'find {imunifyPath} -name "execute.py" -type f -exec chmod +x {{}} \\; 2>/dev/null || true' Upgrade.stdOut(f"Find command: {command}") findResult = Upgrade.executioner(command, 'Find method chmod', 0) Upgrade.stdOut(f"Find result: {findResult}") if findResult and not restored: Upgrade.stdOut("SUCCESS: Find method worked!") restored = True restored = True # Mark as restored even if files are missing, to indicate we processed it if restored: Upgrade.stdOut("Imunify360 restoration completed successfully") else: Upgrade.stdOut("No Imunify360 components found to restore") except Exception as e: Upgrade.stdOut(f"Error during Imunify360 restoration: {str(e)}") @staticmethod def finalImunifyPermissions(): """FINAL STEP: Ensure Imunify360 execute permissions are set after everything else is complete""" try: Upgrade.stdOut("=== FINAL STEP: Setting Imunify360 Execute Permissions ===") executeFile = '/usr/local/CyberCP/public/imunify/bin/execute.py' if os.path.exists(executeFile): Upgrade.stdOut(f"Setting execute permissions on: {executeFile}") # Use the simplest, most reliable command command = f'chmod +x {executeFile}' result = Upgrade.executioner(command, f'Final chmod +x on {executeFile}', 0) if result: Upgrade.stdOut("✅ SUCCESS: Imunify360 execute permissions set successfully!") else: Upgrade.stdOut("❌ FAILED: Could not set Imunify360 execute permissions") # Verify the permissions were set try: import stat file_stat = os.stat(executeFile) if file_stat.st_mode & stat.S_IXUSR: Upgrade.stdOut("✅ VERIFIED: Execute permission confirmed on Imunify360 file") else: Upgrade.stdOut("❌ VERIFICATION FAILED: Execute permission not set") except Exception as verify_error: Upgrade.stdOut(f"⚠️ Could not verify permissions: {str(verify_error)}") else: Upgrade.stdOut(f"⚠️ Imunify360 execute file not found: {executeFile}") Upgrade.stdOut("=== FINAL STEP COMPLETE ===") except Exception as e: Upgrade.stdOut(f"❌ ERROR in final permission setting: {str(e)}") Upgrade.installDNS_CyberPanelACMEFile() command = 'systemctl restart fastapi_ssh_server' Upgrade.executioner(command, command, 0) Upgrade.stdOut("Upgrade Completed.") ### remove log file path incase its there if Upgrade.SoftUpgrade: time.sleep(30) if os.path.exists(Upgrade.LogPathNew): os.remove(Upgrade.LogPathNew) @staticmethod def fixApacheConfigurationOld(): """OLD VERSION - DO NOT USE - Fix Apache configuration issues after upgrade""" try: # Check if Apache is installed if Upgrade.FindOperatingSytem() == CENTOS7 or Upgrade.FindOperatingSytem() == CENTOS8 \ or Upgrade.FindOperatingSytem() == openEuler20 or Upgrade.FindOperatingSytem() == openEuler22: apache_service = 'httpd' apache_config_dir = '/etc/httpd' else: apache_service = 'apache2' apache_config_dir = '/etc/apache2' # Check if Apache is installed check_apache = f'systemctl is-enabled {apache_service} 2>/dev/null' result = subprocess.run(check_apache, shell=True, capture_output=True, text=True) if result.returncode == 0: Upgrade.stdOut("Fixing Apache configuration...") # 1. Ensure Apache ports are correctly configured command = 'grep -q "Listen 8083" /usr/local/lsws/conf/httpd_config.xml || echo "Apache port configuration might need manual check"' Upgrade.executioner(command, 'Check Apache ports', 1) # 2. Fix proxy rewrite rules for all vhosts # The issue: Both rewrite rules execute, causing incorrect proxying # Fix: Add proper HTTPS condition for SSL proxy rule command = '''find /usr/local/lsws/conf/vhosts/ -name "vhost.conf" -exec sed -i ' /^REWRITERULE.*proxyApacheBackendSSL/i\\ RewriteCond %{HTTPS} =on ' {} \;''' Upgrade.executioner(command, 'Fix Apache SSL proxy condition', 1) # Also ensure the proxy backends are properly configured command = '''grep -q "extprocessor apachebackend" /usr/local/lsws/conf/httpd_config.conf || echo " extprocessor apachebackend { type proxy address http://127.0.0.1:8083 maxConns 100 initTimeout 60 retryTimeout 30 respBuffer 0 } extprocessor proxyApacheBackendSSL { type proxy address https://127.0.0.1:8082 maxConns 100 initTimeout 60 retryTimeout 30 respBuffer 0 }" >> /usr/local/lsws/conf/httpd_config.conf''' Upgrade.executioner(command, 'Ensure Apache proxy backends exist', 1) # 3. Ensure Apache is configured to listen on correct ports if Upgrade.FindOperatingSytem() in [CENTOS7, CENTOS8, openEuler20, openEuler22]: apache_port_conf = '/etc/httpd/conf.d/00-port.conf' else: apache_port_conf = '/etc/apache2/ports.conf' command = f''' grep -q "Listen 8082" {apache_port_conf} || echo "Listen 8082" >> {apache_port_conf} grep -q "Listen 8083" {apache_port_conf} || echo "Listen 8083" >> {apache_port_conf} ''' Upgrade.executioner(command, 'Ensure Apache listens on 8082/8083', 1) # 4. Restart Apache service command = f'systemctl restart {apache_service}' Upgrade.executioner(command, f'Restart {apache_service}', 1) # 5. Fix PHP-FPM socket permissions and restart services for version in ['5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']: if Upgrade.FindOperatingSytem() in [CENTOS7, CENTOS8, openEuler20, openEuler22]: php_service = f'php{version.replace(".", "")}-php-fpm' socket_dir = '/var/run/php-fpm' else: php_service = f'php{version}-fpm' socket_dir = '/var/run/php' # Ensure socket directory exists with correct permissions command = f''' if systemctl is-active {php_service} >/dev/null 2>&1; then mkdir -p {socket_dir} chmod 755 {socket_dir} systemctl restart {php_service} fi ''' Upgrade.executioner(command, f'Fix and restart {php_service}', 1) # 6. Reload LiteSpeed to apply proxy changes command = '/usr/local/lsws/bin/lswsctrl reload' Upgrade.executioner(command, 'Reload LiteSpeed', 1) Upgrade.stdOut("Apache configuration fixes completed.") else: Upgrade.stdOut("Apache not detected, skipping Apache fixes.") except Exception as e: Upgrade.stdOut(f"Error fixing Apache configuration: {str(e)}") pass @staticmethod def installQuota(): try: if Upgrade.FindOperatingSytem() == CENTOS7 or Upgrade.FindOperatingSytem() == CENTOS8\ or Upgrade.FindOperatingSytem() == openEuler20 or Upgrade.FindOperatingSytem() == openEuler22: command = "yum install quota -y" Upgrade.executioner(command, command, 0, True) if Upgrade.edit_fstab('/', '/') == 0: print("Quotas will not be abled as we failed to modify fstab file.") return 0 command = 'mount -o remount /' try: mResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: mResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) if mResult.returncode != 0: fstab_path = '/etc/fstab' backup_path = fstab_path + '.bak' if os.path.exists(fstab_path): os.remove(fstab_path) shutil.copy(backup_path, fstab_path) print("Re-mount failed, restoring original FSTab and existing quota setup.") return 0 ## if Upgrade.FindOperatingSytem() == Ubuntu22 or Upgrade.FindOperatingSytem() == Ubuntu24 or Upgrade.FindOperatingSytem() == Ubuntu18 \ or Upgrade.FindOperatingSytem() == Ubuntu20 or Upgrade.FindOperatingSytem() == Debian11 or Upgrade.FindOperatingSytem() == Debian12 or Upgrade.FindOperatingSytem() == Debian13: print("Install Quota on Ubuntu") command = 'apt update -y' Upgrade.executioner(command, command, 0, True) command = 'apt install quota -y' Upgrade.executioner(command, command, 0, True) command = "find /lib/modules/ -type f -name '*quota_v*.ko*'" try: output = subprocess.check_output(command, shell=True) if output and output.decode("utf-8").find("quota/") == -1: command = "sudo apt install linux-image-extra-virtual -y" Upgrade.executioner(command, command, 0, True) except Exception as e: Upgrade.stdOut(f"Error checking quota modules: {str(e)}") if Upgrade.edit_fstab('/', '/') == 0: print("Quotas will not be abled as we are are failed to modify fstab file.") return 0 command = 'mount -o remount /' try: mResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: mResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) if mResult.returncode != 0: fstab_path = '/etc/fstab' backup_path = fstab_path + '.bak' if os.path.exists(fstab_path): os.remove(fstab_path) shutil.copy(backup_path, fstab_path) print("Re-mount failed, restoring original FSTab and existing quota setup.") return 0 command = 'quotacheck -ugm /' try: mResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: mResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) if mResult.returncode != 0: fstab_path = '/etc/fstab' backup_path = fstab_path + '.bak' if os.path.exists(fstab_path): os.remove(fstab_path) shutil.copy(backup_path, fstab_path) print("Re-mount failed, restoring original FSTab and existing quota setup.") return 0 #### command = "find /lib/modules/ -type f -name '*quota_v*.ko*'" try: iResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: iResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) print(repr(iResult.stdout)) # Only if the first command works, run the rest if iResult.returncode == 0: command = "echo '{}' | sed -n 's|/lib/modules/\\([^/]*\\)/.*|\\1|p' | sort -u".format(iResult.stdout) try: result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) fResult = result.stdout.rstrip('\n') print(repr(result.stdout.rstrip('\n'))) command = 'uname -r' try: ffResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: ffResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) ffResult = ffResult.stdout.rstrip('\n') command = f"apt-get install linux-modules-extra-{ffResult}" Upgrade.executioner(command, command, 0, True) ### command = f'modprobe quota_v1 -S {ffResult}' try: mResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: mResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) if mResult.returncode != 0: fstab_path = '/etc/fstab' backup_path = fstab_path + '.bak' if os.path.exists(fstab_path): os.remove(fstab_path) shutil.copy(backup_path, fstab_path) print("Re-mount failed, restoring original FSTab and existing quota setup.") return 0 command = f'modprobe quota_v2 -S {ffResult}' try: mResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: mResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) if mResult.returncode != 0: fstab_path = '/etc/fstab' backup_path = fstab_path + '.bak' if os.path.exists(fstab_path): os.remove(fstab_path) shutil.copy(backup_path, fstab_path) print("Re-mount failed, restoring original FSTab and existing quota setup.") return 0 command = f'quotacheck -ugm /' try: mResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: mResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) if mResult.returncode != 0: fstab_path = '/etc/fstab' backup_path = fstab_path + '.bak' if os.path.exists(fstab_path): os.remove(fstab_path) shutil.copy(backup_path, fstab_path) print("Re-mount failed, restoring original FSTab and existing quota setup.") return 0 command = f'quotaon -v /' try: mResult = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except: mResult = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) if mResult.returncode != 0: fstab_path = '/etc/fstab' backup_path = fstab_path + '.bak' if os.path.exists(fstab_path): os.remove(fstab_path) shutil.copy(backup_path, fstab_path) print("Re-mount failed, restoring original FSTab and existing quota setup.") return 0 return 1 except Exception as e: ErrorSanitizer.log_error_securely(e, 'installQuota') print("[ERROR] installQuota. Failed to install quota") return 0 @staticmethod def edit_fstab(mount_point, options_to_add): try: retValue = 1 # Backup the original fstab file fstab_path = '/etc/fstab' backup_path = fstab_path + '.bak' rData = open(fstab_path, 'r').read() if rData.find('xfs') > -1: options_to_add = 'uquota' else: options_to_add = 'usrquota,grpquota' if not os.path.exists(backup_path): shutil.copy(fstab_path, backup_path) # Read the fstab file with open(fstab_path, 'r') as file: lines = file.readlines() # Modify the appropriate line WriteToFile = open(fstab_path, 'w') for i, line in enumerate(lines): if line.find('\t') > -1: parts = line.split('\t') else: parts = line.split(' ') print(parts) try: if parts[1] == '/' and parts[3].find(options_to_add) == -1 and len(parts[3]) > 4: parts[3] = f'{parts[3]},{options_to_add}' tempParts = [item for item in parts if item.strip()] finalString = '\t'.join(tempParts) print(finalString) WriteToFile.write(finalString) elif parts[1] == '/': for ii, p in enumerate(parts): if p.find('defaults') > -1 or p.find('discard') > -1 or p.find('errors=') > -1: parts[ii] = f'{parts[ii]},{options_to_add}' tempParts = [item for item in parts if item.strip()] finalString = '\t'.join(tempParts) print(finalString) WriteToFile.write(finalString) else: WriteToFile.write(line) except: WriteToFile.write(line) WriteToFile.close() return retValue except: return 0 @staticmethod def FixCurrentQuoatasSystem(): fstab_path = '/etc/fstab' data = open(fstab_path, 'r').read() if data.find("usrquota,grpquota") > -1 or data.find("uquota") > -1: print("Quotas already enabled.") if Upgrade.installQuota() == 1: print("We will attempt to bring new Quota system to old websites.") from websiteFunctions.models import Websites for website in Websites.objects.all(): command = 'chattr -R -i /home/%s/' % (website.domain) Upgrade.executioner(command, command, 0, True) if website.package.enforceDiskLimits: spaceString = f'{website.package.diskSpace}M {website.package.diskSpace}M' command = f'setquota -u {website.externalApp} {spaceString} 0 0 /' Upgrade.executioner(command, command, 0, True) else: print("Quotas can not be enabled continue to use chhtr.") @staticmethod def installDNS_CyberPanelACMEFile(): filePath = '/root/.acme.sh/dns_cyberpanel.sh' if os.path.exists(filePath): os.remove(filePath) shutil.copy('/usr/local/CyberCP/install/dns_cyberpanel.sh', filePath) command = f'chmod +x {filePath}' Upgrade.executioner(command, command, 0, True) @staticmethod def fixApacheConfiguration(): """ Fix Apache configuration issues after upgrade, particularly for 503 errors when Apache is used as reverse proxy to OpenLiteSpeed """ try: print("Starting Apache configuration fix...") # Check if Apache is installed osType = Upgrade.FindOperatingSytem() if osType in [CENTOS7, CENTOS8, CloudLinux7, CloudLinux8]: configBasePath = '/etc/httpd/conf.d/' serviceName = 'httpd' else: configBasePath = '/etc/apache2/sites-enabled/' serviceName = 'apache2' if not os.path.exists(configBasePath): print("Apache not installed, skipping Apache fixes.") return # Import required modules from websiteFunctions.models import Websites import re # Fix 1: Update Apache proxy configurations for domains actually using Apache print("Fixing Apache proxy configurations...") fixed_count = 0 apache_domains = [] # First, identify which domains are using Apache by checking for Apache vhost configs for config_file in os.listdir(configBasePath): if config_file.endswith('.conf'): # Extract domain name from config file domain_name = config_file.replace('.conf', '') config_path = os.path.join(configBasePath, config_file) try: # Read the configuration to verify it's an Apache proxy setup with open(config_path, 'r') as f: content = f.read() # Check if this is actually an Apache proxy configuration # Look for common Apache proxy indicators is_apache_proxy = False if 'ProxyPass' in content and ('127.0.0.1:8082' in content or '127.0.0.1:8083' in content): is_apache_proxy = True elif 'RewriteRule' in content and 'apachebackend' in content: is_apache_proxy = True elif ' 0: print(f"Fixed {ols_fixed} OpenLiteSpeed vhost configurations.") # Fix 4: Ensure Apache is listening on correct ports if osType in [CENTOS7, CENTOS8, CloudLinux7, CloudLinux8]: apache_conf = '/etc/httpd/conf/httpd.conf' else: ports_conf = '/etc/apache2/ports.conf' apache_conf = ports_conf if os.path.exists(ports_conf) else '/etc/apache2/apache2.conf' if os.path.exists(apache_conf): with open(apache_conf, 'r') as f: conf_content = f.read() # Check if Apache is configured to listen on 8082 and 8083 if 'Listen 8082' not in conf_content or 'Listen 8083' not in conf_content: print("Fixing Apache listen ports...") # For Ubuntu/Debian, update ports.conf if osType not in [CENTOS7, CENTOS8, CloudLinux7, CloudLinux8]: if os.path.exists('/etc/apache2/ports.conf'): with open('/etc/apache2/ports.conf', 'w') as f: f.write('Listen 8082\nListen 8083\n') else: # For CentOS, update httpd.conf lines = conf_content.split('\n') new_lines = [] listen_added = False for line in lines: if line.strip().startswith('Listen') and '80' in line and not listen_added: new_lines.append('Listen 8082') new_lines.append('Listen 8083') listen_added = True elif 'Listen 8082' not in line and 'Listen 8083' not in line: new_lines.append(line) with open(apache_conf, 'w') as f: f.write('\n'.join(new_lines)) print("Fixed Apache listen ports") # Fix 5: Fix PHP-FPM socket permissions print("Fixing PHP-FPM socket permissions...") if osType in [CENTOS7, CENTOS8, CloudLinux7, CloudLinux8]: sock_path = '/var/run/php-fpm/' else: sock_path = '/var/run/php/' if os.path.exists(sock_path): # Set proper permissions command = f'chmod 755 {sock_path}' Upgrade.executioner(command, command, 0, True) # Fix ownership command = f'chown apache:apache {sock_path}' if osType in [CENTOS7, CENTOS8, CloudLinux7, CloudLinux8] else f'chown www-data:www-data {sock_path}' Upgrade.executioner(command, command, 0, True) # Restart services print("Restarting services...") # Restart Apache command = f'systemctl restart {serviceName}' Upgrade.executioner(command, command, 0, True) # Restart OpenLiteSpeed command = 'systemctl restart lsws' Upgrade.executioner(command, command, 0, True) # Restart PHP-FPM services if osType in [CENTOS7, CENTOS8, CloudLinux7, CloudLinux8]: for version in ['54', '55', '56', '70', '71', '72', '73', '74', '80', '81', '82', '83', '84']: command = f'systemctl restart php{version}-php-fpm' Upgrade.executioner(command, command, 0, True) else: for version in ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']: command = f'systemctl restart php{version}-fpm' Upgrade.executioner(command, command, 0, True) print("Apache configuration fix completed successfully!") except Exception as e: print(f"Error during Apache configuration fix: {str(e)}") def main(): parser = argparse.ArgumentParser(description='CyberPanel Installer') parser.add_argument('branch', help='Install from branch name.') args = parser.parse_args() Upgrade.upgrade(args.branch) if __name__ == "__main__": main()