#!/opt/imh-python/bin/python3 """ Script to perform post-restoration tasks for CWP after transferring from cPanel. """ import os import re import subprocess def create_tmp_folder_for_users(): """ This function creates a tmp folder in each user's home directory if it does not already exist. """ home_dir = '/home' for user_dir in os.listdir(home_dir): user_path = os.path.join(home_dir, user_dir) tmp_dir = os.path.join(user_path, 'tmp') if os.path.isdir(user_path): # Check if tmp directory exists if not os.path.exists(tmp_dir): try: os.makedirs(tmp_dir) # Adjust ownership to match the user’s own UID/GID os.chown( tmp_dir, os.stat(user_path).st_uid, os.stat(user_path).st_gid, ) # Set strict permissions (700) os.chmod(tmp_dir, 0o700) print(f"[INFO] Created {tmp_dir} for user {user_dir}") except Exception as e: print( f"[ERROR] Failed to create {tmp_dir} " f"for user {user_dir}: {e}" ) else: print(f"[INFO] Skipping {tmp_dir}: already exists") def comment_out_directives(file_path): """ This function comments out lines containing session.save_path directives that match the pattern "/var/cpanel/php/sessions/ea-php*" and AddHandler application/x-httpd-ea-php* in the given file. """ try: with open(file_path, encoding='utf-8') as file: lines = file.readlines() with open(file_path, 'w', encoding='utf-8') as file: for line in lines: # Comment out session.save_path directives with specific # patterns if re.search( r'^\s*(php_value\s+)?session\.save_path\s*(=|\s)\s*"[^"]*' r'/var/cpanel/php/sessions/ea-php[^"]*"', line, ): file.write('#' + line) # Comment out the line # Comment out AddHandler directives for ea-php elif re.search( r'^\s*AddHandler\s+application/x-httpd-ea-php\d+.*$', line ): file.write('#' + line) # Comment out the line else: file.write(line) # Write the line as-is print(f"[INFO] Updated {file_path}") except Exception as e: print(f"[ERROR] Failed to update {file_path}: {e}") def find_and_update_files(root_dir): """ This function finds .htaccess, php.ini, and .user.ini files in the given directory and updates them by commenting out session.save_path and AddHandler directives. """ for subdir, _, files in os.walk(root_dir): for filename in files: if filename in ['.htaccess', 'php.ini', '.user.ini']: file_path = os.path.join(subdir, filename) comment_out_directives(file_path) def get_document_roots(): """ This function gets the document roots for all users on the system, which typically are /home//public_html. """ home_dir = '/home' document_roots = [] for user_dir in os.listdir(home_dir): public_html = os.path.join(home_dir, user_dir, 'public_html') if os.path.isdir(public_html): document_roots.append(public_html) return document_roots def get_addon_document_roots_from_db(): """ This function checks the 'root_cwp.domains' table for domain paths, and returns them as a list. """ domain_paths = [] try: # fmt: off output = subprocess.check_output([ 'mysql', '-u', 'root', '-N', '-B', '-e', 'SELECT path FROM root_cwp.domains', ]).decode('utf-8').strip() # fmt: on for line in output.splitlines(): line = line.strip() # If the path exists, append it if line and os.path.isdir(line): domain_paths.append(line) else: # If the path doesn't exist on the filesystem, print(f"[WARN] Skipping domain path (not found): {line}") except subprocess.CalledProcessError as exc: print( "[ERROR] Failed to retrieve domain paths from " f"root_cwp.domains: {exc}" ) return domain_paths def get_subdomain_document_roots_from_db(): """ This function checks the 'root_cwp.subdomains' table for domain/path pairs, and returns them as a list of valid local paths. """ domain_paths = [] try: # fmt: off output = subprocess.check_output([ 'mysql', '-u', 'root', '-N', '-B', '-e', 'SELECT domain, path FROM root_cwp.subdomains', ]).decode('utf-8').strip() # fmt: on # Each line should contain something like: # mysub.example.com /home/username/public_html/sub for line in output.splitlines(): parts = line.split('\t') if len(parts) == 2: domain, path = parts[0].strip(), parts[1].strip() if path and os.path.isdir(path): domain_paths.append(path) else: print( f"[WARN] Skipping subdomain path (not found): {path} " f"(domain: {domain})" ) else: print( f"[WARN] Unexpected line format in subdomains output:" f" {line}" ) except subprocess.CalledProcessError as exc: print( "[ERROR] Failed to retrieve domain paths from root_cwp.subdomains:" f" {exc}" ) return domain_paths def fix_permissions_for_users(): """ This function executes the 'fix_perms' command for each user in the /home directory. (Mail fix_permissions) """ home_dir = '/home' for user_dir in os.listdir(home_dir): user_path = os.path.join(home_dir, user_dir) if os.path.isdir(user_path): username = os.path.basename(user_path) try: # Fix file permissions for this user subprocess.run( ['/scripts/cwp_api', 'account', 'fix_perms', username], check=True, ) print(f"[INFO] Fixed permissions for {username}") except subprocess.CalledProcessError as e: print(f"[ERROR] Failed to fix permissions for {username}: {e}") def import_mail_vmail(): """ This function runs /scripts/mail_vmail_import. """ try: subprocess.run(['/scripts/mail_vmail_import'], check=True) print("[INFO] Successfully ran /scripts/mail_vmail_import") except subprocess.CalledProcessError as e: print(f"[ERROR] Failed to run /scripts/mail_vmail_import: {e}") def remove_symlinks_in_vmail(): """ This function removes all symbolic links under /var/vmail recursively. """ vmail_dir = '/var/vmail' for dirpath, dirnames, filenames in os.walk(vmail_dir): for name in dirnames + filenames: path = os.path.join(dirpath, name) if os.path.islink(path): try: os.unlink(path) print(f"[INFO] Removed symlink: {path}") except Exception as e: print(f"[ERROR] Failed to remove symlink {path}: {e}") def remove_hidden_folders_in_vmail(): """ This function removes all hidden folders under /var/vmail. """ try: # fmt: off subprocess.run( [ 'find', '/var/vmail', '-mindepth', '1', '-type', 'd', '-name', '.*', '-exec', 'rm', '-rf', '{}', '+' ], check=True, ) # fmt: on print("[INFO] Removed all hidden folders under /var/vmail") except subprocess.CalledProcessError as e: print(f"[ERROR] Failed to remove hidden folders in /var/vmail: {e}") def main(): """ Main function to: 1. Create tmp folders for each user. 2. Fix file permissions for each user (only file perms, not mail perms). 3. Fix mail permissions once for ALL accounts. 4. Change ownership of /var/vmail to vmail:mail. 5. Import mail data. 6. Find and update .htaccess/php.ini/.user.ini files in all user document roots as well as any additional domain roots from the CWP domains table and now also from the subdomains table. """ # Create tmp folder for each user create_tmp_folder_for_users() # Fix file permissions for each user fix_permissions_for_users() # Now fix mail permissions once for ALL accounts (no username needed) try: subprocess.run( ['/scripts/cwp_api', 'account', 'mail_fix_permissions'], check=True ) print("[INFO] Fixed mail permissions for all accounts") except subprocess.CalledProcessError as e: print(f"[ERROR] Failed to fix mail permissions for all accounts: {e}") # Import mail data remove_hidden_folders_in_vmail() remove_symlinks_in_vmail() import_mail_vmail() # Collect default /home/*/public_html doc roots document_roots = get_document_roots() # Collect additional domain roots from the root_cwp.domains table addon_roots = get_addon_document_roots_from_db() # Collect subdomain roots from root_cwp.subdomains subdomain_roots = get_subdomain_document_roots_from_db() # Combine them all_document_roots = document_roots + addon_roots + subdomain_roots # 5. Update .htaccess, php.ini, .user.ini in each discovered path for root_dir in all_document_roots: find_and_update_files(root_dir) if __name__ == "__main__": main()