diff --git a/filemanager/filemanager.py b/filemanager/filemanager.py index 9c89c2fb2..1a6068e70 100644 --- a/filemanager/filemanager.py +++ b/filemanager/filemanager.py @@ -384,16 +384,25 @@ class FileManager: website = Websites.objects.get(domain=domainName) self.homePath = '/home/%s' % (domainName) + logging.CyberCPLogFileWriter.writeToFile(f"Attempting to delete files/folders for domain: {domainName}") + RemoveOK = 1 + # Test if directory is writable command = 'touch %s/public_html/hello.txt' % (self.homePath) result = ProcessUtilities.outputExecutioner(command) if result.find('cannot touch') > -1: RemoveOK = 0 + logging.CyberCPLogFileWriter.writeToFile(f"Directory {self.homePath} is not writable, removing chattr flags") + # Remove immutable flag from entire directory command = 'chattr -R -i %s' % (self.homePath) - ProcessUtilities.executioner(command) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to remove chattr -i from {self.homePath}: {result}") + else: + logging.CyberCPLogFileWriter.writeToFile(f"Successfully removed chattr -i from {self.homePath}") else: command = 'rm -f %s/public_html/hello.txt' % (self.homePath) @@ -401,111 +410,155 @@ class FileManager: for item in self.data['fileAndFolders']: + itemPath = self.data['path'] + '/' + item + + # Security check - prevent path traversal + if itemPath.find('..') > -1 or itemPath.find(self.homePath) == -1: + logging.CyberCPLogFileWriter.writeToFile(f"Security violation: Attempted to delete outside home directory: {itemPath}") + return self.ajaxPre(0, 'Not allowed to delete files outside home directory!') - if (self.data['path'] + '/' + item).find('..') > -1 or (self.data['path'] + '/' + item).find( - self.homePath) == -1: - return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!') + logging.CyberCPLogFileWriter.writeToFile(f"Deleting: {itemPath}") if skipTrash: - command = 'rm -rf ' + self.returnPathEnclosed(self.data['path'] + '/' + item) - ProcessUtilities.executioner(command, website.externalApp) + # Permanent deletion + command = 'rm -rf ' + self.returnPathEnclosed(itemPath) + result = ProcessUtilities.executioner(command, website.externalApp) + if result.find('cannot') > -1 or result.find('Permission denied') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to delete {itemPath}: {result}") + # Try with sudo if available + command = 'sudo rm -rf ' + self.returnPathEnclosed(itemPath) + result = ProcessUtilities.executioner(command, website.externalApp) + if result.find('cannot') > -1 or result.find('Permission denied') > -1: + return self.ajaxPre(0, f'Failed to delete {item}: {result}') + logging.CyberCPLogFileWriter.writeToFile(f"Successfully deleted: {itemPath}") else: + # Move to trash trashPath = '%s/.trash' % (self.homePath) - command = 'mkdir %s' % (trashPath) - ProcessUtilities.executioner(command, website.externalApp) + # Ensure trash directory exists + command = 'mkdir -p %s' % (trashPath) + result = ProcessUtilities.executioner(command, website.externalApp) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to create trash directory: {result}") + return self.ajaxPre(0, f'Failed to create trash directory: {result}') - Trash(website=website, originalPath=self.returnPathEnclosed(self.data['path']), - fileName=self.returnPathEnclosed(item)).save() + # Save to trash database + try: + Trash(website=website, originalPath=self.returnPathEnclosed(self.data['path']), + fileName=self.returnPathEnclosed(item)).save() + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to save trash record: {str(e)}") - command = 'mv %s %s' % (self.returnPathEnclosed(self.data['path'] + '/' + item), trashPath) - ProcessUtilities.executioner(command, website.externalApp) + # Move to trash + command = 'mv %s %s' % (self.returnPathEnclosed(itemPath), trashPath) + result = ProcessUtilities.executioner(command, website.externalApp) + if result.find('cannot') > -1 or result.find('Permission denied') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to move to trash {itemPath}: {result}") + # Try with sudo if available + command = 'sudo mv %s %s' % (self.returnPathEnclosed(itemPath), trashPath) + result = ProcessUtilities.executioner(command, website.externalApp) + if result.find('cannot') > -1 or result.find('Permission denied') > -1: + return self.ajaxPre(0, f'Failed to move {item} to trash: {result}') + logging.CyberCPLogFileWriter.writeToFile(f"Successfully moved to trash: {itemPath}") if RemoveOK == 0: + logging.CyberCPLogFileWriter.writeToFile(f"Restoring chattr +i flags for {self.homePath}") + + # Restore immutable flag to entire directory command = 'chattr -R +i %s' % (self.homePath) - ProcessUtilities.executioner(command) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to restore chattr +i to {self.homePath}: {result}") + else: + logging.CyberCPLogFileWriter.writeToFile(f"Successfully restored chattr +i to {self.homePath}") # Allow specific directories to remain mutable - command = 'chattr -R -i %s' % (self.homePath) + '/logs/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/.trash/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/backup/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/incbackup/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/lscache/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/.cagefs/' - ProcessUtilities.executioner(command) - except: + mutable_dirs = ['/logs/', '/.trash/', '/backup/', '/incbackup/', '/lscache/', '/.cagefs/'] + for dir_name in mutable_dirs: + dir_path = self.homePath + dir_name + command = 'chattr -R -i %s' % (dir_path) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to remove chattr +i from {dir_path}: {result}") + else: + logging.CyberCPLogFileWriter.writeToFile(f"Successfully removed chattr +i from {dir_path}") + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error in deleteFolderOrFile for {domainName}: {str(e)}") try: skipTrash = self.data['skipTrash'] except: skipTrash = False + # Fallback to root path for system files self.homePath = '/' + logging.CyberCPLogFileWriter.writeToFile(f"Using fallback deletion for system files in {self.data['path']}") RemoveOK = 1 + # Test if directory is writable command = 'touch %s/public_html/hello.txt' % (self.homePath) result = ProcessUtilities.outputExecutioner(command) if result.find('cannot touch') > -1: RemoveOK = 0 + logging.CyberCPLogFileWriter.writeToFile(f"Directory {self.homePath} is not writable, removing chattr flags") command = 'chattr -R -i %s' % (self.homePath) - ProcessUtilities.executioner(command) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to remove chattr -i from {self.homePath}: {result}") else: command = 'rm -f %s/public_html/hello.txt' % (self.homePath) ProcessUtilities.executioner(command) for item in self.data['fileAndFolders']: + itemPath = self.data['path'] + '/' + item + + # Security check for system files + if itemPath.find('..') > -1 or itemPath.find(self.homePath) == -1: + logging.CyberCPLogFileWriter.writeToFile(f"Security violation: Attempted to delete outside allowed path: {itemPath}") + return self.ajaxPre(0, 'Not allowed to delete files outside allowed path!') - if (self.data['path'] + '/' + item).find('..') > -1 or (self.data['path'] + '/' + item).find( - self.homePath) == -1: - return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!') + logging.CyberCPLogFileWriter.writeToFile(f"Deleting system file: {itemPath}") if skipTrash: - command = 'rm -rf ' + self.returnPathEnclosed(self.data['path'] + '/' + item) - ProcessUtilities.executioner(command) + command = 'rm -rf ' + self.returnPathEnclosed(itemPath) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1 or result.find('Permission denied') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to delete system file {itemPath}: {result}") + return self.ajaxPre(0, f'Failed to delete {item}: {result}') + logging.CyberCPLogFileWriter.writeToFile(f"Successfully deleted system file: {itemPath}") if RemoveOK == 0: + logging.CyberCPLogFileWriter.writeToFile(f"Restoring chattr +i flags for system path: {self.homePath}") command = 'chattr -R +i %s' % (self.homePath) - ProcessUtilities.executioner(command) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to restore chattr +i to system path {self.homePath}: {result}") + else: + logging.CyberCPLogFileWriter.writeToFile(f"Successfully restored chattr +i to system path {self.homePath}") - # Allow specific directories to remain mutable - command = 'chattr -R -i %s' % (self.homePath) + '/logs/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/.trash/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/backup/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/incbackup/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/lscache/' - ProcessUtilities.executioner(command) - - command = 'chattr -R -i %s' % (self.homePath) + '/.cagefs/' - ProcessUtilities.executioner(command) + # Allow specific directories to remain mutable for system files + mutable_dirs = ['/logs/', '/.trash/', '/backup/', '/incbackup/', '/lscache/', '/.cagefs/'] + for dir_name in mutable_dirs: + dir_path = self.homePath + dir_name + command = 'chattr -R -i %s' % (dir_path) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to remove chattr +i from system {dir_path}: {result}") + else: + logging.CyberCPLogFileWriter.writeToFile(f"Successfully removed chattr +i from system {dir_path}") + logging.CyberCPLogFileWriter.writeToFile(f"File deletion completed successfully for domain: {domainName}") json_data = json.dumps(finalData) return HttpResponse(json_data) except BaseException as msg: - return self.ajaxPre(0, str(msg)) + logging.CyberCPLogFileWriter.writeToFile(f"Critical error in deleteFolderOrFile: {str(msg)}") + return self.ajaxPre(0, f"File deletion failed: {str(msg)}") def restore(self): try: diff --git a/plogical/IncScheduler.py b/plogical/IncScheduler.py index 57e73df8b..00d14f0a1 100644 --- a/plogical/IncScheduler.py +++ b/plogical/IncScheduler.py @@ -1152,6 +1152,12 @@ Automatic backup failed for %s on %s. # # command = 'chattr -R -i /home/%s/incbackup/' % (website.domain) # ProcessUtilities.executioner(command) + # + # command = 'chattr -R -i /home/%s/lscache/' % (website.domain) + # ProcessUtilities.executioner(command) + # + # command = 'chattr -R -i /home/%s/.cagefs/' % (website.domain) + # ProcessUtilities.executioner(command) # else: # command = 'chattr -R -i /home/%s/' % (website.domain) # ProcessUtilities.executioner(command) diff --git a/websiteFunctions/website.py b/websiteFunctions/website.py index 3230e804e..dd36358bf 100644 --- a/websiteFunctions/website.py +++ b/websiteFunctions/website.py @@ -2890,6 +2890,17 @@ Require valid-user # Ensure suspension page exists and has proper permissions suspensionPagePath = "/usr/local/CyberCP/websiteFunctions/suspension.html" + suspensionDir = "/usr/local/CyberCP/websiteFunctions" + + # Ensure directory exists + if not os.path.exists(suspensionDir): + try: + command = f"mkdir -p {suspensionDir}" + ProcessUtilities.executioner(command) + logging.CyberCPLogFileWriter.writeToFile(f"Created suspension directory: {suspensionDir}") + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to create suspension directory: {str(e)}") + if not os.path.exists(suspensionPagePath): # Create default suspension page if it doesn't exist defaultSuspensionHTML = """ @@ -2958,17 +2969,34 @@ Require valid-user # Use ProcessUtilities to move the file to the final location command = f"mv {tempFile} {suspensionPagePath}" ProcessUtilities.executioner(command) - except: - pass + logging.CyberCPLogFileWriter.writeToFile(f"Created suspension page: {suspensionPagePath}") + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to create suspension page: {str(e)}") + # Try alternative method using echo command + try: + command = f'echo "{defaultSuspensionHTML.replace('"', '\\"')}" > {suspensionPagePath}' + ProcessUtilities.executioner(command) + logging.CyberCPLogFileWriter.writeToFile(f"Created suspension page using echo: {suspensionPagePath}") + except Exception as e2: + logging.CyberCPLogFileWriter.writeToFile(f"Failed to create suspension page with echo: {str(e2)}") + return self.ajaxPre(0, f"Failed to create suspension page: {str(e2)}") # Set proper permissions for suspension page try: command = f"chown lsadm:lsadm {suspensionPagePath}" - ProcessUtilities.executioner(command) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to set ownership for suspension page: {result}") + command = f"chmod 644 {suspensionPagePath}" - ProcessUtilities.executioner(command) - except: - pass + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to set permissions for suspension page: {result}") + + logging.CyberCPLogFileWriter.writeToFile(f"Set permissions for suspension page: {suspensionPagePath}") + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Error setting suspension page permissions: {str(e)}") + # Don't fail the entire operation for permission issues # Create suspension configuration with end marker suspensionConf = """# Website Suspension Configuration @@ -2999,10 +3027,18 @@ context /cyberpanel_suspension_page.html { """ try: + # Check if vhost file exists + if not os.path.exists(vhostConfPath): + logging.CyberCPLogFileWriter.writeToFile(f"Error: Vhost configuration file not found: {vhostConfPath}") + return self.ajaxPre(0, f"Vhost configuration file not found for {websiteName}") + # Read current vhost configuration with open(vhostConfPath, 'r') as f: vhostContent = f.read() + if not vhostContent.strip(): + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Empty vhost configuration file: {vhostConfPath}") + if "# Website Suspension Configuration" not in vhostContent: # Check if there's an existing rewrite block at the root level # If so, we need to comment it out to avoid conflicts @@ -3026,7 +3062,11 @@ context /cyberpanel_suspension_page.html { # Set proper ownership command = f"chown lsadm:lsadm {vhostConfPath}" - ProcessUtilities.executioner(command) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to set vhost ownership: {result}") + + logging.CyberCPLogFileWriter.writeToFile(f"Successfully suspended website: {websiteName}") except IOError as e: # If direct file access fails, fall back to command-based approach command = f"cat {vhostConfPath}" @@ -3115,17 +3155,34 @@ context /cyberpanel_suspension_page.html { except Exception as e: CyberCPLogFileWriter.writeToFile(f"Error suspending child domain {items.domain}: {str(e)}") - installUtilities.reStartLiteSpeedSocket() - website.state = 0 + # Restart LiteSpeed to apply changes + try: + installUtilities.reStartLiteSpeedSocket() + logging.CyberCPLogFileWriter.writeToFile(f"Restarted LiteSpeed after suspending {websiteName}") + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to restart LiteSpeed: {str(e)}") + + website.state = 1 else: + # Unsuspend logic confPath = virtualHostUtilities.Server_root + "/conf/vhosts/" + websiteName vhostConfPath = confPath + "/vhost.conf" + logging.CyberCPLogFileWriter.writeToFile(f"Attempting to unsuspend website: {websiteName}") + try: + # Check if vhost file exists + if not os.path.exists(vhostConfPath): + logging.CyberCPLogFileWriter.writeToFile(f"Error: Vhost configuration file not found: {vhostConfPath}") + return self.ajaxPre(0, f"Vhost configuration file not found for {websiteName}") + # Try direct file access first with open(vhostConfPath, 'r') as f: vhostContent = f.read() + if not vhostContent.strip(): + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Empty vhost configuration file: {vhostConfPath}") + if "# Website Suspension Configuration" in vhostContent: # Use regex to remove the suspension configuration block pattern = r'# Website Suspension Configuration.*?# End Website Suspension Configuration\n' @@ -3150,7 +3207,13 @@ context /cyberpanel_suspension_page.html { f.write(modifiedContent) command = f"chown lsadm:lsadm {vhostConfPath}" - ProcessUtilities.executioner(command) + result = ProcessUtilities.executioner(command) + if result.find('cannot') > -1: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to set vhost ownership: {result}") + + logging.CyberCPLogFileWriter.writeToFile(f"Successfully unsuspended website: {websiteName}") + else: + logging.CyberCPLogFileWriter.writeToFile(f"Website {websiteName} is not currently suspended") except IOError: # Fall back to command-based approach command = f"cat {vhostConfPath}" @@ -3240,8 +3303,14 @@ context /cyberpanel_suspension_page.html { except Exception as e: CyberCPLogFileWriter.writeToFile(f"Error unsuspending child domain {items.domain}: {str(e)}") - installUtilities.reStartLiteSpeedSocket() - website.state = 1 + # Restart LiteSpeed to apply changes + try: + installUtilities.reStartLiteSpeedSocket() + logging.CyberCPLogFileWriter.writeToFile(f"Restarted LiteSpeed after unsuspending {websiteName}") + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f"Warning: Failed to restart LiteSpeed: {str(e)}") + + website.state = 0 website.save()