mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-02-05 22:29:05 +01:00
- Fix delete for domain and Root File Manager: use sudo helper when lscpd/executioner fails (TOKEN/sendCommand issues) - Add safe-delete-path and safe-move-path helpers for base64 path handling - Add ACLManager.isPathInsideHome and isFilePathSafeForShell for path validation - Fix upload authorization for Root File Manager (domainName empty) - Harden outputExecutioner result checks to prevent 500 on None - Update Bootstrap CDN for CSP compatibility - Improve error display and a11y focus management in modals - Resolves #1670: files with special characters can be uploaded/deleted
1342 lines
66 KiB
Python
1342 lines
66 KiB
Python
import os
|
|
import base64
|
|
|
|
from django.shortcuts import HttpResponse
|
|
import json
|
|
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
|
from plogical.processUtilities import ProcessUtilities
|
|
from websiteFunctions.models import Websites
|
|
from random import randint
|
|
from django.core.files.storage import FileSystemStorage
|
|
from plogical.acl import ACLManager
|
|
from filemanager.models import Trash
|
|
|
|
|
|
class FileManager:
|
|
modes = {'php': 'application/x-httpd-php', 'javascript': 'javascript', 'python': 'text/x-python',
|
|
'html': 'text/html', 'go': 'text/x-go', 'css': 'text/css', 'java': 'text/x-java', 'perl': 'text/x-perl',
|
|
'scss': 'text/x-sass'}
|
|
|
|
def __init__(self, request, data):
|
|
self.request = request
|
|
self.data = data
|
|
|
|
@staticmethod
|
|
def findMode(fileName):
|
|
if fileName.endswith('.php'):
|
|
return FileManager.modes['php']
|
|
elif fileName.endswith('.js'):
|
|
return FileManager.modes['javascript']
|
|
elif fileName.endswith('.py'):
|
|
return FileManager.modes['python']
|
|
elif fileName.endswith('.html'):
|
|
return FileManager.modes['html']
|
|
elif fileName.endswith('.go'):
|
|
return FileManager.modes['go']
|
|
elif fileName.endswith('.css'):
|
|
return FileManager.modes['css']
|
|
elif fileName.endswith('.pl') or fileName.endswith('.PL'):
|
|
return FileManager.modes['perl']
|
|
elif fileName.endswith('.java'):
|
|
return FileManager.modes['java']
|
|
elif fileName.endswith('.scss'):
|
|
return FileManager.modes['scss']
|
|
else:
|
|
return ""
|
|
|
|
|
|
@staticmethod
|
|
def findModeFiles(mode):
|
|
|
|
if mode == FileManager.modes['php']:
|
|
return """
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/javascript/javascript.min.js"
|
|
integrity="sha512-e3U/84Fo+2ZAnRhLkjStm2hYnkmZ/NRmeesZ/GHjDhcLh35eYTQxsfSeDppx6Se5aX0N6mrygH7tr4wugWsPeQ=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/javascript-hint.min.js"
|
|
integrity="sha512-PPI9W6pViVZfJ5uvmYZsHbPwf7T+voS0OpohIrN8Q4CRCCa6JK39JJ0R16HHmyV7EQR8MTa+O56CpWjfKOxl0A=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/css/css.min.js"
|
|
integrity="sha512-DG+5u//fVN9kpDgTGe78IJhJW8e5+tlrPaMgNqcrzyPXsn+GPaF2T62+X3ds7SuhFR9Qeb7XZ6kMD8X09FeJhA=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/xml/xml.min.js"
|
|
integrity="sha512-k1HnoY9EXahEfPz7kq/lD9DltloKH9OrB9XNKYoUQrNz9epe5F4mQP5PfuIfeRfoXHkNrE0gF3Mx4LhC5BVl9Q=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/htmlmixed/htmlmixed.min.js"
|
|
integrity="sha512-p15qsXPrhaUkH+/RPE6QzCmxUAPkCRw89ityx+tWC1lAYI6Et2L0UpN+iqifxUdt+ss1FQ+9CuzxpBeT9mR3/w=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/clike/clike.min.js" integrity="sha512-HT3t3u7HfQ7USbSZa0Tk5caEnUfO8s58OWqMBwm96xaZAbA17rpnXXHDefR8ixVmSSVssbOv3W3OMh6mNX/XuQ==" crossorigin="anonymous"></script>
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/anyword-hint.min.js" integrity="sha512-wdYOcbX/zcS4tP3HEDTkdOI5UybyuRxJMQzDQIRcafRLY/oTDWyXO+P8SzuajQipcJXkb2vHcd1QetccSFAaVw==" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/css-hint.min.js" integrity="sha512-iXuwWkAmdAUNuO5rUtzmJZ/LoeJoSG8ZeQVdcUBCkV0dxfe7bxfzQMKCwQ6uNNs0FZ9jmSrN/jzJX7G1bOs4Nw==" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/html-hint.min.js" integrity="sha512-aGi2Yn9VkLP9HiwiMXfkY7KQoGfwDW6JiGUtPhiPJAL9J7+rwwPVWUtUYvHW+xp3yJ7F0UhTPoPumUZv3+E/Rg==" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/sql-hint.min.js" integrity="sha512-zVNOyYBOmDcGRo9/Tz+rYW8vjhAO4D/jqbj9+IIb1xWMU1ROyNWPCeWcOoBTquOBBmdiue78xJg5kkdWzsZJog==" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/xml-hint.min.js" integrity="sha512-XtLGFClKrm3hNY3bS01LPiIkF64i9CnlxCqj5O+TSQq7UW8kFhFIc3kOR3bJ98h4ThxFaKdJA9PpQC76LvD/oQ==" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/php/php.min.js"
|
|
integrity="sha512-m8sosGXUwyH6Ppzoy+CoQ/r5zAwZRGdNFUgGH81E3RDQkFnAsE4cP1I3tokvZwgMsDZB5mHxs+7egAgvhaCcMw=="
|
|
crossorigin="anonymous"></script>
|
|
"""
|
|
elif mode == FileManager.modes['javascript']:
|
|
return """
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/show-hint.min.js"
|
|
integrity="sha512-ge9uKCpgPmuJY2e2zPXhpYCZfyb1/R7KOOfMZ3SzSX3ZayWpINs3sHnI8LGEHUf6UOFX/D03FVHgR36uRL8/Vw=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/javascript/javascript.min.js"
|
|
integrity="sha512-e3U/84Fo+2ZAnRhLkjStm2hYnkmZ/NRmeesZ/GHjDhcLh35eYTQxsfSeDppx6Se5aX0N6mrygH7tr4wugWsPeQ=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/javascript-hint.min.js"
|
|
integrity="sha512-PPI9W6pViVZfJ5uvmYZsHbPwf7T+voS0OpohIrN8Q4CRCCa6JK39JJ0R16HHmyV7EQR8MTa+O56CpWjfKOxl0A=="
|
|
crossorigin="anonymous"></script>
|
|
"""
|
|
elif mode == FileManager.modes['python']:
|
|
return """
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/python/python.min.js" integrity="sha512-DS+asaww1mE0V/N6YGVgoNIRj+yXB9hAV68vM6rVeWs0G+OyMd24LKrnS4Z+g26rgghU7qvGeEnRVUArV7nVog==" crossorigin="anonymous"></script>
|
|
"""
|
|
elif mode == FileManager.modes['html']:
|
|
return """
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/javascript/javascript.min.js"
|
|
integrity="sha512-e3U/84Fo+2ZAnRhLkjStm2hYnkmZ/NRmeesZ/GHjDhcLh35eYTQxsfSeDppx6Se5aX0N6mrygH7tr4wugWsPeQ=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/javascript-hint.min.js"
|
|
integrity="sha512-PPI9W6pViVZfJ5uvmYZsHbPwf7T+voS0OpohIrN8Q4CRCCa6JK39JJ0R16HHmyV7EQR8MTa+O56CpWjfKOxl0A=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/css/css.min.js"
|
|
integrity="sha512-DG+5u//fVN9kpDgTGe78IJhJW8e5+tlrPaMgNqcrzyPXsn+GPaF2T62+X3ds7SuhFR9Qeb7XZ6kMD8X09FeJhA=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/xml/xml.min.js"
|
|
integrity="sha512-k1HnoY9EXahEfPz7kq/lD9DltloKH9OrB9XNKYoUQrNz9epe5F4mQP5PfuIfeRfoXHkNrE0gF3Mx4LhC5BVl9Q=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/htmlmixed/htmlmixed.min.js"
|
|
integrity="sha512-p15qsXPrhaUkH+/RPE6QzCmxUAPkCRw89ityx+tWC1lAYI6Et2L0UpN+iqifxUdt+ss1FQ+9CuzxpBeT9mR3/w=="
|
|
crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/anyword-hint.min.js" integrity="sha512-wdYOcbX/zcS4tP3HEDTkdOI5UybyuRxJMQzDQIRcafRLY/oTDWyXO+P8SzuajQipcJXkb2vHcd1QetccSFAaVw==" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/css-hint.min.js" integrity="sha512-iXuwWkAmdAUNuO5rUtzmJZ/LoeJoSG8ZeQVdcUBCkV0dxfe7bxfzQMKCwQ6uNNs0FZ9jmSrN/jzJX7G1bOs4Nw==" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/html-hint.min.js" integrity="sha512-aGi2Yn9VkLP9HiwiMXfkY7KQoGfwDW6JiGUtPhiPJAL9J7+rwwPVWUtUYvHW+xp3yJ7F0UhTPoPumUZv3+E/Rg==" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/sql-hint.min.js" integrity="sha512-zVNOyYBOmDcGRo9/Tz+rYW8vjhAO4D/jqbj9+IIb1xWMU1ROyNWPCeWcOoBTquOBBmdiue78xJg5kkdWzsZJog==" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/addon/hint/xml-hint.min.js" integrity="sha512-XtLGFClKrm3hNY3bS01LPiIkF64i9CnlxCqj5O+TSQq7UW8kFhFIc3kOR3bJ98h4ThxFaKdJA9PpQC76LvD/oQ==" crossorigin="anonymous"></script>
|
|
"""
|
|
elif mode == FileManager.modes['go']:
|
|
return """
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/go/go.min.js" integrity="sha512-DxeIplahS44UYHUdqtsLJ21g5xHilhuP7Y4i+NSsD7J4ow+LXIXLHsjvEpMqcTSg15rkaqBRIXEETAjq3yb5Cw==" crossorigin="anonymous"></script>
|
|
"""
|
|
elif mode == FileManager.modes['css']:
|
|
return """
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/css/css.min.js" integrity="sha512-DG+5u//fVN9kpDgTGe78IJhJW8e5+tlrPaMgNqcrzyPXsn+GPaF2T62+X3ds7SuhFR9Qeb7XZ6kMD8X09FeJhA==" crossorigin="anonymous"></script>
|
|
"""
|
|
elif mode == FileManager.modes['java']:
|
|
return """
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/clike/clike.min.js" integrity="sha512-HT3t3u7HfQ7USbSZa0Tk5caEnUfO8s58OWqMBwm96xaZAbA17rpnXXHDefR8ixVmSSVssbOv3W3OMh6mNX/XuQ==" crossorigin="anonymous"></script>
|
|
"""
|
|
elif mode == FileManager.modes['perl']:
|
|
return """
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/perl/perl.min.js" integrity="sha512-6rKFA1mIjmFqxMM/b0dtjQOWFRAoqKCmhb7/6u2KohJcP4poKbrUI08Yf5GXsK+rkCr2dQnppV7gMe2a0HGQBQ==" crossorigin="anonymous"></script>
|
|
"""
|
|
elif mode == FileManager.modes['scss']:
|
|
return """
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/mode/sass/sass.min.js" integrity="sha512-lFZETu8ovGFrFbFWAJnwgJrRcQ06C0BhjySIpBFPUatL/vqFz/mZIvXhlLtbOwbvRCp+XcLCmTEigKOJPN+YhA==" crossorigin="anonymous"></script>
|
|
"""
|
|
else:
|
|
return ''
|
|
|
|
@staticmethod
|
|
def findThemeFile(theme):
|
|
return '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.1/theme/%s.min.css" />' % (theme)
|
|
|
|
@staticmethod
|
|
def findAdditionalOptions(mode):
|
|
if mode == 'text/x-python':
|
|
return """<select ng-model="optionValue" ng-change="additionalOptions()">
|
|
<option>Python 2</option>
|
|
<option>Python 3</option>
|
|
</select>
|
|
"""
|
|
else:
|
|
return ""
|
|
|
|
def ajaxPre(self, status, errorMessage):
|
|
final_dic = {'status': status, 'error_message': errorMessage, 'uploadStatus': status}
|
|
final_json = json.dumps(final_dic)
|
|
return HttpResponse(final_json)
|
|
|
|
def returnPathEnclosed(self, path):
|
|
return "'" + path + "'"
|
|
|
|
def _moveViaPythonBase64(self, src_path, dest_path, user):
|
|
"""Fallback: use helper script or Python to move when mv fails (handles special chars)."""
|
|
try:
|
|
import subprocess
|
|
s_b64 = base64.b64encode(src_path.encode('utf-8')).decode('ascii')
|
|
d_b64 = base64.b64encode(dest_path.encode('utf-8')).decode('ascii')
|
|
helper = '/usr/local/CyberCP/bin/safe-move-path'
|
|
if os.path.isfile(helper) and os.access(helper, os.X_OK):
|
|
cmd = [helper, s_b64, d_b64]
|
|
if os.getuid() != 0:
|
|
cmd = ['sudo', '-n'] + cmd
|
|
try:
|
|
res = subprocess.run(cmd, capture_output=True, timeout=30)
|
|
if res.returncode == 0:
|
|
return True
|
|
except Exception as e:
|
|
logging.writeToFile(f"_moveViaPythonBase64 sudo helper failed: {str(e)}")
|
|
command = '%s %s %s' % (helper, s_b64, d_b64)
|
|
else:
|
|
code = "import shutil,base64,sys; s=base64.b64decode(sys.argv[1]).decode(); d=base64.b64decode(sys.argv[2]).decode(); shutil.move(s,d)"
|
|
command = "/usr/bin/python3 -c '%s' %s %s" % (code, s_b64, d_b64)
|
|
result = ProcessUtilities.executioner(command, user)
|
|
return result == 1
|
|
except Exception as e:
|
|
logging.writeToFile(f"_moveViaPythonBase64 failed: {str(e)}")
|
|
return False
|
|
|
|
def _deleteViaPythonBase64(self, path, user):
|
|
"""Fallback: use helper script or Python to delete when rm fails (handles special chars)."""
|
|
try:
|
|
import subprocess
|
|
p_b64 = base64.b64encode(path.encode('utf-8')).decode('ascii')
|
|
helper = '/usr/local/CyberCP/bin/safe-delete-path'
|
|
if os.path.isfile(helper) and os.access(helper, os.X_OK):
|
|
cmd = [helper, p_b64]
|
|
if os.getuid() != 0:
|
|
cmd = ['sudo', '-n'] + cmd
|
|
try:
|
|
res = subprocess.run(cmd, capture_output=True, timeout=30)
|
|
if res.returncode == 0:
|
|
return True
|
|
except Exception as e:
|
|
logging.writeToFile(f"_deleteViaPythonBase64 sudo helper failed: {str(e)}")
|
|
if os.path.isfile(helper) and os.access(helper, os.X_OK):
|
|
command = '%s %s' % (helper, p_b64)
|
|
else:
|
|
code = "import os,base64,sys,shutil; p=base64.b64decode(sys.argv[1]).decode(); (os.path.isfile(p) and os.remove(p)) or (os.path.isdir(p) and shutil.rmtree(p))"
|
|
command = "/usr/bin/python3 -c '%s' %s" % (code, p_b64)
|
|
result = ProcessUtilities.executioner(command, user)
|
|
return result == 1
|
|
except Exception as e:
|
|
logging.writeToFile(f"_deleteViaPythonBase64 failed: {str(e)}")
|
|
return False
|
|
|
|
def changeOwner(self, path):
|
|
try:
|
|
domainName = self.data['domainName']
|
|
website = Websites.objects.get(domain=domainName)
|
|
homePath = '/home/%s' % (domainName)
|
|
|
|
if not ACLManager.isPathInsideHome(path, homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = "chown -R " + website.externalApp + ':' + website.externalApp + ' ' + self.returnPathEnclosed(path)
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
except:
|
|
print("Permisson not changed")
|
|
|
|
|
|
def listForTable(self):
|
|
try:
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
|
|
try:
|
|
|
|
domainName = self.data['domainName']
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
pathCheck = '/home/%s' % (domainName)
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['completeStartingPath'], pathCheck):
|
|
return self.ajaxPre(0, 'Not allowed to browse this path, going back home!')
|
|
|
|
command = "ls -la --group-directories-first " + self.returnPathEnclosed(
|
|
self.data['completeStartingPath'])
|
|
output = ProcessUtilities.outputExecutioner(command, website.externalApp).splitlines()
|
|
|
|
except:
|
|
pathCheck = '/'
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['completeStartingPath'], pathCheck):
|
|
return self.ajaxPre(0, 'Not allowed to browse this path, going back home!')
|
|
|
|
command = "ls -la --group-directories-first " + self.returnPathEnclosed(
|
|
self.data['completeStartingPath'])
|
|
output = ProcessUtilities.outputExecutioner(command).splitlines()
|
|
|
|
counter = 0
|
|
for items in output:
|
|
try:
|
|
currentFile = items.split(' ')
|
|
currentFile = [a for a in currentFile if a != '']
|
|
|
|
if currentFile[-1] == '.' or currentFile[-1] == '..' or currentFile[0] == 'total':
|
|
continue
|
|
|
|
if len(currentFile) > 9:
|
|
fileName = currentFile[8:]
|
|
currentFile[-1] = " ".join(fileName)
|
|
|
|
dirCheck = 0
|
|
if currentFile[0][0] == 'd':
|
|
dirCheck = 1
|
|
|
|
size = str(int(int(currentFile[4]) / float(1024)))
|
|
lastModified = currentFile[5] + ' ' + currentFile[6] + ' ' + currentFile[7]
|
|
finalData[str(counter)] = [currentFile[-1], currentFile[-1], lastModified, size, currentFile[0],
|
|
dirCheck]
|
|
counter = counter + 1
|
|
except BaseException as msg:
|
|
logging.writeToFile(str(msg))
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def list(self):
|
|
try:
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
try:
|
|
domainName = self.data['domainName']
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
command = "ls -la --group-directories-first " + self.returnPathEnclosed(
|
|
self.data['completeStartingPath'])
|
|
output = ProcessUtilities.outputExecutioner(command, website.externalApp).splitlines()
|
|
|
|
counter = 0
|
|
for items in output:
|
|
try:
|
|
currentFile = items.split(' ')
|
|
currentFile = [a for a in currentFile if a != '']
|
|
|
|
if currentFile[-1] == '.' or currentFile[-1] == '..' or currentFile[0] == 'total':
|
|
continue
|
|
|
|
if len(currentFile) > 9:
|
|
fileName = currentFile[8:]
|
|
currentFile[-1] = " ".join(fileName)
|
|
|
|
dirCheck = False
|
|
if currentFile[0][0] == 'd':
|
|
dirCheck = True
|
|
|
|
finalData[str(counter)] = [currentFile[-1],
|
|
self.data['completeStartingPath'] + '/' + currentFile[-1], dirCheck]
|
|
counter = counter + 1
|
|
except:
|
|
continue
|
|
except:
|
|
command = "ls -la --group-directories-first " + self.returnPathEnclosed(
|
|
self.data['completeStartingPath'])
|
|
output = ProcessUtilities.outputExecutioner(command).splitlines()
|
|
|
|
counter = 0
|
|
for items in output:
|
|
try:
|
|
currentFile = items.split(' ')
|
|
currentFile = [a for a in currentFile if a != '']
|
|
|
|
if currentFile[-1] == '.' or currentFile[-1] == '..' or currentFile[0] == 'total':
|
|
continue
|
|
|
|
if len(currentFile) > 9:
|
|
fileName = currentFile[8:]
|
|
currentFile[-1] = " ".join(fileName)
|
|
|
|
dirCheck = False
|
|
if currentFile[0][0] == 'd':
|
|
dirCheck = True
|
|
|
|
finalData[str(counter)] = [currentFile[-1],
|
|
self.data['completeStartingPath'] + '/' + currentFile[-1], dirCheck]
|
|
counter = counter + 1
|
|
except:
|
|
continue
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def createNewFile(self):
|
|
try:
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
|
|
try:
|
|
domainName = self.data['domainName']
|
|
website = Websites.objects.get(domain=domainName)
|
|
homePath = '/home/%s' % (domainName)
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['fileName'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = "touch " + self.returnPathEnclosed(self.data['fileName'])
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
self.changeOwner(self.returnPathEnclosed(self.data['fileName']))
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
except:
|
|
homePath = '/'
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['fileName'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = "touch " + self.returnPathEnclosed(self.data['fileName'])
|
|
ProcessUtilities.executioner(command)
|
|
self.changeOwner(self.returnPathEnclosed(self.data['fileName']))
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def createNewFolder(self):
|
|
try:
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
try:
|
|
domainName = self.data['domainName']
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
homePath = '/home/%s' % (domainName)
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['folderName'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = "mkdir " + self.returnPathEnclosed(self.data['folderName'])
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
self.changeOwner(self.returnPathEnclosed(self.data['folderName']))
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
except:
|
|
homePath = '/'
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['folderName'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = "mkdir " + self.returnPathEnclosed(self.data['folderName'])
|
|
ProcessUtilities.executioner(command)
|
|
|
|
self.changeOwner(self.returnPathEnclosed(self.data['folderName']))
|
|
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def deleteFolderOrFile(self):
|
|
try:
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
domainName = self.data['domainName']
|
|
try:
|
|
try:
|
|
skipTrash = self.data['skipTrash']
|
|
except:
|
|
skipTrash = False
|
|
|
|
website = Websites.objects.get(domain=domainName)
|
|
self.homePath = '/home/%s' % (domainName)
|
|
|
|
logging.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 is None:
|
|
result = ''
|
|
|
|
if isinstance(result, (str, bytes)) and ('cannot touch' in str(result) or 'Permission denied' in str(result)):
|
|
RemoveOK = 0
|
|
logging.writeToFile(f"Directory {self.homePath} is not writable, removing chattr flags")
|
|
|
|
# Remove immutable flag from entire directory (executioner returns 1=success, 0=failure)
|
|
command = 'chattr -R -i %s' % (self.homePath)
|
|
result = ProcessUtilities.executioner(command)
|
|
if result != 1:
|
|
logging.writeToFile(f"Warning: Failed to remove chattr -i from {self.homePath}: {result}")
|
|
else:
|
|
logging.writeToFile(f"Successfully removed chattr -i from {self.homePath}")
|
|
|
|
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 - prevent path traversal
|
|
if not ACLManager.isPathInsideHome(itemPath, self.homePath):
|
|
logging.writeToFile(f"Security violation: Attempted to delete outside home directory: {itemPath}")
|
|
return self.ajaxPre(0, 'Not allowed to delete files outside home directory!')
|
|
|
|
logging.writeToFile(f"Deleting: {itemPath}")
|
|
|
|
if skipTrash:
|
|
# Permanent deletion (executioner returns 1=success, 0=failure)
|
|
command = 'rm -rf ' + self.returnPathEnclosed(itemPath)
|
|
result = ProcessUtilities.executioner(command, website.externalApp)
|
|
if result != 1:
|
|
logging.writeToFile(f"Failed to delete {itemPath}: result={result}, trying Python fallback")
|
|
# Fallback: Python+base64 to handle special chars in paths
|
|
if not self._deleteViaPythonBase64(itemPath, website.externalApp):
|
|
return self.ajaxPre(0, f'Failed to delete {item}')
|
|
logging.writeToFile(f"Successfully deleted: {itemPath}")
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
else:
|
|
# Move to trash
|
|
trashPath = '%s/.trash' % (self.homePath)
|
|
|
|
# Ensure trash directory exists (executioner returns 1=success, 0=failure)
|
|
command = 'mkdir -p %s' % (trashPath)
|
|
result = ProcessUtilities.executioner(command, website.externalApp)
|
|
if result != 1:
|
|
logging.writeToFile(f"Failed to create trash directory: result={result}")
|
|
return self.ajaxPre(0, f'Failed to create trash directory')
|
|
|
|
# Save to trash database
|
|
try:
|
|
Trash(website=website, originalPath=self.returnPathEnclosed(self.data['path']),
|
|
fileName=self.returnPathEnclosed(item)).save()
|
|
except Exception as e:
|
|
logging.writeToFile(f"Failed to save trash record: {str(e)}")
|
|
|
|
# Move to trash (executioner returns 1=success, 0=failure)
|
|
command = 'mv %s %s' % (self.returnPathEnclosed(itemPath), trashPath)
|
|
result = ProcessUtilities.executioner(command, website.externalApp)
|
|
if result != 1:
|
|
logging.writeToFile(f"Failed to move to trash {itemPath}: result={result}, trying Python fallback")
|
|
# Fallback: Python+base64 to handle special chars in paths (e.g. lscpd/sendCommand)
|
|
if not self._moveViaPythonBase64(itemPath, trashPath, website.externalApp):
|
|
return self.ajaxPre(0, f'Failed to move {item} to trash')
|
|
logging.writeToFile(f"Successfully moved to trash: {itemPath}")
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
if RemoveOK == 0:
|
|
logging.writeToFile(f"Restoring chattr +i flags for {self.homePath}")
|
|
|
|
# Restore immutable flag to entire directory (executioner returns 1=success, 0=failure)
|
|
command = 'chattr -R +i %s' % (self.homePath)
|
|
result = ProcessUtilities.executioner(command)
|
|
if result != 1:
|
|
logging.writeToFile(f"Warning: Failed to restore chattr +i to {self.homePath}: result={result}")
|
|
else:
|
|
logging.writeToFile(f"Successfully restored chattr +i to {self.homePath}")
|
|
|
|
# Allow specific directories to remain mutable
|
|
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 != 1:
|
|
logging.writeToFile(f"Warning: Failed to remove chattr +i from {dir_path}: result={result}")
|
|
else:
|
|
logging.writeToFile(f"Successfully removed chattr +i from {dir_path}")
|
|
except Exception as e:
|
|
import traceback
|
|
logging.writeToFile(f"Error in deleteFolderOrFile for {domainName}: {str(e)}")
|
|
logging.writeToFile(traceback.format_exc())
|
|
try:
|
|
skipTrash = self.data['skipTrash']
|
|
except:
|
|
skipTrash = False
|
|
|
|
# Fallback to root path for system files (Root File Manager, domainName empty)
|
|
self.homePath = '/'
|
|
logging.writeToFile(f"Using fallback deletion for system files in {self.data['path']}")
|
|
|
|
RemoveOK = 1
|
|
|
|
# Test if we can write (use /tmp for root path since /public_html doesn't exist at /)
|
|
test_path = '/tmp' if self.homePath == '/' else (self.homePath + '/public_html')
|
|
command = 'touch %s/hello.txt' % (test_path)
|
|
result = ProcessUtilities.outputExecutioner(command)
|
|
if result is None:
|
|
result = ''
|
|
|
|
if isinstance(result, (str, bytes)) and ('cannot touch' in str(result) or 'Permission denied' in str(result)):
|
|
RemoveOK = 0
|
|
logging.writeToFile(f"Directory {self.homePath} is not writable, removing chattr flags")
|
|
|
|
command = 'chattr -R -i %s' % (self.homePath)
|
|
result = ProcessUtilities.executioner(command)
|
|
if result != 1:
|
|
logging.writeToFile(f"Warning: Failed to remove chattr -i from {self.homePath}: {result}")
|
|
|
|
else:
|
|
command = 'rm -f %s/hello.txt' % (test_path)
|
|
ProcessUtilities.executioner(command)
|
|
|
|
for item in self.data['fileAndFolders']:
|
|
base = self.data['path'].rstrip('/') or '/'
|
|
itemPath = base + '/' + item
|
|
|
|
# Security check for system files
|
|
if not ACLManager.isPathInsideHome(itemPath, self.homePath):
|
|
logging.writeToFile(f"Security violation: Attempted to delete outside allowed path: {itemPath}")
|
|
return self.ajaxPre(0, 'Not allowed to delete files outside allowed path!')
|
|
|
|
logging.writeToFile(f"Deleting system file: {itemPath}")
|
|
|
|
if skipTrash:
|
|
command = 'rm -rf ' + self.returnPathEnclosed(itemPath)
|
|
result = ProcessUtilities.executioner(command)
|
|
if result != 1:
|
|
logging.writeToFile(f"Failed to delete system file {itemPath}: result={result}, trying Python fallback")
|
|
if not self._deleteViaPythonBase64(itemPath, None):
|
|
return self.ajaxPre(0, f'Failed to delete {item}')
|
|
logging.writeToFile(f"Successfully deleted system file: {itemPath}")
|
|
|
|
|
|
if RemoveOK == 0:
|
|
logging.writeToFile(f"Restoring chattr +i flags for system path: {self.homePath}")
|
|
command = 'chattr -R +i %s' % (self.homePath)
|
|
result = ProcessUtilities.executioner(command)
|
|
if result != 1:
|
|
logging.writeToFile(f"Warning: Failed to restore chattr +i to system path {self.homePath}: result={result}")
|
|
else:
|
|
logging.writeToFile(f"Successfully restored chattr +i to system path {self.homePath}")
|
|
|
|
# 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 != 1:
|
|
logging.writeToFile(f"Warning: Failed to remove chattr +i from system {dir_path}: result={result}")
|
|
else:
|
|
logging.writeToFile(f"Successfully removed chattr +i from system {dir_path}")
|
|
|
|
logging.writeToFile(f"File deletion completed successfully for domain: {domainName}")
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
logging.writeToFile(f"Critical error in deleteFolderOrFile: {str(msg)}")
|
|
return self.ajaxPre(0, f"File deletion failed: {str(msg)}")
|
|
|
|
def restore(self):
|
|
try:
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
|
|
domainName = self.data['domainName']
|
|
|
|
try:
|
|
skipTrash = self.data['skipTrash']
|
|
except:
|
|
skipTrash = False
|
|
|
|
website = Websites.objects.get(domain=domainName)
|
|
self.homePath = '/home/%s' % (domainName)
|
|
|
|
for item in self.data['fileAndFolders']:
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['path'] + '/' + item, self.homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
trashPath = '%s/.trash' % (self.homePath)
|
|
|
|
tItem = Trash.objects.get(website=website, fileName=self.returnPathEnclosed(item))
|
|
|
|
command = 'mv %s %s' % (self.returnPathEnclosed(trashPath + '/' + item), tItem.originalPath)
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
tItem.delete()
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def copy(self):
|
|
try:
|
|
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
|
|
domainName = self.data['domainName']
|
|
try:
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
homePath = '/home/%s' % (domainName)
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['newPath'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if len(self.data['fileAndFolders']) == 1:
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + self.data['fileAndFolders'][0], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = 'yes| cp -Rf %s %s' % (
|
|
self.returnPathEnclosed(self.data['basePath'] + '/' + self.data['fileAndFolders'][0]),
|
|
self.data['newPath'])
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
self.changeOwner(self.data['newPath'])
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
command = 'mkdir ' + self.returnPathEnclosed(self.data['newPath'])
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
for item in self.data['fileAndFolders']:
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + item, homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = '%scp -Rf ' % ('yes |') + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + item) + ' ' + self.returnPathEnclosed(self.data['newPath'])
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
self.changeOwner(self.data['newPath'])
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
except:
|
|
|
|
|
|
homePath = '/'
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['newPath'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if len(self.data['fileAndFolders']) == 1:
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + self.data['fileAndFolders'][0], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = 'yes| cp -Rf %s %s' % (
|
|
self.returnPathEnclosed(self.data['basePath'] + '/' + self.data['fileAndFolders'][0]),
|
|
self.data['newPath'])
|
|
ProcessUtilities.executioner(command,)
|
|
self.changeOwner(self.data['newPath'])
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
command = 'mkdir ' + self.returnPathEnclosed(self.data['newPath'])
|
|
ProcessUtilities.executioner(command)
|
|
|
|
for item in self.data['fileAndFolders']:
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + item, homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = '%scp -Rf ' % ('yes |') + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + item) + ' ' + self.returnPathEnclosed(self.data['newPath'])
|
|
ProcessUtilities.executioner(command)
|
|
|
|
self.changeOwner(self.data['newPath'])
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def move(self):
|
|
try:
|
|
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
domainName = self.data['domainName']
|
|
try:
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
homePath = '/home/%s' % (domainName)
|
|
|
|
command = 'mkdir ' + self.returnPathEnclosed(self.data['newPath'])
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
for item in self.data['fileAndFolders']:
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + item, homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['newPath'] + '/' + item, homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = 'mv ' + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + item) + ' ' + self.returnPathEnclosed(
|
|
self.data['newPath'] + '/' + item)
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
#self.changeOwner(self.data['newPath'])
|
|
|
|
#self.fixPermissions(domainName)
|
|
except:
|
|
|
|
|
|
homePath = '/'
|
|
|
|
command = 'mkdir ' + self.returnPathEnclosed(self.data['newPath'])
|
|
ProcessUtilities.executioner(command)
|
|
|
|
for item in self.data['fileAndFolders']:
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + item, homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['newPath'] + '/' + item, homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = 'mv ' + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + item) + ' ' + self.returnPathEnclosed(
|
|
self.data['newPath'] + '/' + item)
|
|
ProcessUtilities.executioner(command)
|
|
|
|
self.changeOwner(self.data['newPath'])
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def rename(self):
|
|
try:
|
|
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
domainName = self.data['domainName']
|
|
try:
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
homePath = '/home/%s' % (domainName)
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + self.data['existingName'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + self.data['newFileName'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = 'mv ' + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['existingName']) + ' ' + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['newFileName'])
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
self.changeOwner(self.data['basePath'] + '/' + self.data['newFileName'])
|
|
except:
|
|
homePath = '/'
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + self.data['existingName'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + '/' + self.data['newFileName'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = 'mv ' + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['existingName']) + ' ' + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['newFileName'])
|
|
ProcessUtilities.executioner(command)
|
|
|
|
self.changeOwner(self.data['basePath'] + '/' + self.data['newFileName'])
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def readFileContents(self):
|
|
try:
|
|
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
domainName = self.data['domainName']
|
|
try:
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
pathCheck = '/home/%s' % (domainName)
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['fileName'], pathCheck):
|
|
return self.ajaxPre(0, 'Not allowed.')
|
|
|
|
# Ensure proper UTF-8 handling for file reading
|
|
# Use explicit UTF-8 locale for the cat command
|
|
command = 'LANG=C.UTF-8 LC_ALL=C.UTF-8 cat ' + self.returnPathEnclosed(self.data['fileName'])
|
|
finalData['fileContents'] = ProcessUtilities.outputExecutioner(command, website.externalApp)
|
|
except:
|
|
pathCheck = '/'
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['fileName'], pathCheck):
|
|
return self.ajaxPre(0, 'Not allowed.')
|
|
|
|
# Ensure proper UTF-8 handling for file reading
|
|
# Use explicit UTF-8 locale for the cat command
|
|
command = 'LANG=C.UTF-8 LC_ALL=C.UTF-8 cat ' + self.returnPathEnclosed(self.data['fileName'])
|
|
finalData['fileContents'] = ProcessUtilities.outputExecutioner(command)
|
|
|
|
|
|
# Ensure proper UTF-8 encoding in JSON response
|
|
json_data = json.dumps(finalData, ensure_ascii=False)
|
|
return HttpResponse(json_data, content_type='application/json; charset=utf-8')
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def writeFileContents(self):
|
|
try:
|
|
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
try:
|
|
self.data['home'] = '/home/%s' % (self.data['domainName'])
|
|
|
|
ACLManager.CreateSecureDir()
|
|
tempPath = '%s/%s' % ('/usr/local/CyberCP/tmp', str(randint(1000, 9999)))
|
|
|
|
domainName = self.data['domainName']
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
writeToFile = open(tempPath, 'wb')
|
|
writeToFile.write(self.data['fileContent'].encode('utf-8'))
|
|
writeToFile.close()
|
|
|
|
command = 'chown %s:%s %s' % (website.externalApp, website.externalApp, tempPath)
|
|
ProcessUtilities.executioner(command)
|
|
|
|
command = 'cp %s %s' % (tempPath, self.returnPathEnclosed(self.data['fileName']))
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
os.remove(tempPath)
|
|
except:
|
|
self.data['home'] = '/'
|
|
|
|
ACLManager.CreateSecureDir()
|
|
tempPath = '%s/%s' % ('/usr/local/CyberCP/tmp', str(randint(1000, 9999)))
|
|
writeToFile = open(tempPath, 'wb')
|
|
writeToFile.write(self.data['fileContent'].encode('utf-8'))
|
|
writeToFile.close()
|
|
|
|
command = 'cp %s %s' % (tempPath, self.returnPathEnclosed(self.data['fileName']))
|
|
ProcessUtilities.executioner(command)
|
|
|
|
os.remove(tempPath)
|
|
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def upload(self):
|
|
try:
|
|
|
|
finalData = {}
|
|
finalData['uploadStatus'] = 1
|
|
finalData['answer'] = 'File transfer completed.'
|
|
|
|
ACLManager.CreateSecureDir()
|
|
UploadPath = '/usr/local/CyberCP/tmp/'
|
|
|
|
## Random file name
|
|
|
|
RanddomFileName = str(randint(1000, 9999))
|
|
|
|
myfile = self.request.FILES['file']
|
|
fs = FileSystemStorage()
|
|
|
|
try:
|
|
filename = fs.save(RanddomFileName, myfile)
|
|
finalData['fileName'] = fs.url(filename)
|
|
except BaseException as msg:
|
|
logging.writeToFile('%s. [375:upload]' % (str(msg)))
|
|
|
|
|
|
|
|
domainName = self.data['domainName']
|
|
try:
|
|
pathCheck = '/home/%s' % (self.data['domainName'])
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
command = 'ls -la %s' % (self.data['completePath'])
|
|
result = ProcessUtilities.outputExecutioner(command, website.externalApp)
|
|
#
|
|
if result.find('->') > -1:
|
|
return self.ajaxPre(0, "Symlink attack.")
|
|
|
|
uploadPathFull = self.data['completePath'] + '/' + myfile.name
|
|
if not ACLManager.isFilePathSafeForShell(uploadPathFull):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if not ACLManager.isPathInsideHome(uploadPathFull, pathCheck):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = 'cp ' + self.returnPathEnclosed(
|
|
UploadPath + RanddomFileName) + ' ' + self.returnPathEnclosed(
|
|
self.data['completePath'] + '/' + myfile.name)
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
self.changeOwner(self.returnPathEnclosed(self.data['completePath'] + '/' + myfile.name))
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
try:
|
|
os.remove(UploadPath + RanddomFileName)
|
|
except:
|
|
pass
|
|
except:
|
|
pathCheck = '/'
|
|
command = 'ls -la %s' % (self.data['completePath'])
|
|
result = ProcessUtilities.outputExecutioner(command)
|
|
logging.writeToFile("upload file res %s" % result)
|
|
uploadPathFull = self.data['completePath'] + '/' + myfile.name
|
|
if not ACLManager.isFilePathSafeForShell(uploadPathFull):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if not ACLManager.isPathInsideHome(uploadPathFull, pathCheck):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = 'cp ' + self.returnPathEnclosed(
|
|
UploadPath + RanddomFileName) + ' ' + self.returnPathEnclosed(
|
|
self.data['completePath'] + '/' + myfile.name)
|
|
ProcessUtilities.executioner(command)
|
|
|
|
self.changeOwner(self.returnPathEnclosed(self.data['completePath'] + '/' + myfile.name))
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
try:
|
|
os.remove(UploadPath + RanddomFileName)
|
|
except:
|
|
pass
|
|
|
|
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
try:
|
|
os.remove(UploadPath + RanddomFileName)
|
|
except:
|
|
pass
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def extract(self):
|
|
try:
|
|
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
|
|
domainName = self.data['domainName']
|
|
|
|
try:
|
|
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
homePath = '/home/%s' % (domainName)
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['extractionLocation'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['fileToExtract'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if self.data['extractionType'] == 'zip':
|
|
command = 'unzip -o ' + self.returnPathEnclosed(
|
|
self.data['fileToExtract']) + ' -d ' + self.returnPathEnclosed(self.data['extractionLocation'])
|
|
elif self.data['extractionType'] == '7z':
|
|
command = '7z x ' + self.returnPathEnclosed(
|
|
self.data['fileToExtract']) + ' -o' + self.returnPathEnclosed(self.data['extractionLocation']) + ' -y'
|
|
elif self.data['extractionType'] == 'rar':
|
|
# Try unrar first (free), fallback to 7z if unrar not available
|
|
command = 'unrar x ' + self.returnPathEnclosed(
|
|
self.data['fileToExtract']) + ' ' + self.returnPathEnclosed(self.data['extractionLocation']) + ' -y'
|
|
else:
|
|
command = 'tar -xf ' + self.returnPathEnclosed(
|
|
self.data['fileToExtract']) + ' -C ' + self.returnPathEnclosed(self.data['extractionLocation'])
|
|
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
#self.fixPermissions(domainName)
|
|
except:
|
|
|
|
homePath = '/'
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['extractionLocation'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['fileToExtract'], homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
if self.data['extractionType'] == 'zip':
|
|
command = 'unzip -o ' + self.returnPathEnclosed(
|
|
self.data['fileToExtract']) + ' -d ' + self.returnPathEnclosed(self.data['extractionLocation'])
|
|
elif self.data['extractionType'] == '7z':
|
|
command = '7z x ' + self.returnPathEnclosed(
|
|
self.data['fileToExtract']) + ' -o' + self.returnPathEnclosed(self.data['extractionLocation']) + ' -y'
|
|
elif self.data['extractionType'] == 'rar':
|
|
# Try unrar first (free), fallback to 7z if unrar not available
|
|
command = 'unrar x ' + self.returnPathEnclosed(
|
|
self.data['fileToExtract']) + ' ' + self.returnPathEnclosed(self.data['extractionLocation']) + ' -y'
|
|
else:
|
|
command = 'tar -xf ' + self.returnPathEnclosed(
|
|
self.data['fileToExtract']) + ' -C ' + self.returnPathEnclosed(self.data['extractionLocation'])
|
|
|
|
ProcessUtilities.executioner(command)
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def compress(self):
|
|
try:
|
|
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
domainName = self.data['domainName']
|
|
try:
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
if self.data['compressionType'] == 'zip':
|
|
compressedFileName = self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['compressedFileName'] + '.zip')
|
|
command = 'zip -r ' + compressedFileName + ' '
|
|
elif self.data['compressionType'] == '7z':
|
|
compressedFileName = self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['compressedFileName'] + '.7z')
|
|
command = '7z a -t7z ' + compressedFileName + ' '
|
|
elif self.data['compressionType'] == 'rar':
|
|
compressedFileName = self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['compressedFileName'] + '.rar')
|
|
# Use 7z to create RAR format (7z can create RAR archives)
|
|
command = '7z a -trar ' + compressedFileName + ' '
|
|
else:
|
|
compressedFileName = self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['compressedFileName'] + '.tar.gz')
|
|
command = 'tar -czvf ' + compressedFileName + ' '
|
|
|
|
homePath = '/home/%s' % (domainName)
|
|
|
|
for item in self.data['listOfFiles']:
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + item, homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
|
|
command = '%s%s ' % (command, self.returnPathEnclosed(item))
|
|
|
|
finalCommand = 'cd %s && %s' % (self.data['basePath'], command)
|
|
|
|
ProcessUtilities.executioner(finalCommand, website.externalApp)
|
|
|
|
self.changeOwner(self.data['compressedFileName'])
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
except:
|
|
if self.data['compressionType'] == 'zip':
|
|
compressedFileName = self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['compressedFileName'] + '.zip')
|
|
command = 'zip -r ' + compressedFileName + ' '
|
|
elif self.data['compressionType'] == '7z':
|
|
compressedFileName = self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['compressedFileName'] + '.7z')
|
|
command = '7z a -t7z ' + compressedFileName + ' '
|
|
elif self.data['compressionType'] == 'rar':
|
|
compressedFileName = self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['compressedFileName'] + '.rar')
|
|
# Use 7z to create RAR format (7z can create RAR archives)
|
|
command = '7z a -trar ' + compressedFileName + ' '
|
|
else:
|
|
compressedFileName = self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['compressedFileName'] + '.tar.gz')
|
|
command = 'tar -czvf ' + compressedFileName + ' '
|
|
|
|
homePath = '/'
|
|
|
|
for item in self.data['listOfFiles']:
|
|
|
|
if not ACLManager.isPathInsideHome(self.data['basePath'] + item, homePath):
|
|
return self.ajaxPre(0, 'Not allowed to move in this path, please choose location inside home!')
|
|
command = '%s%s ' % (command, self.returnPathEnclosed(item))
|
|
|
|
finalCommand = 'cd %s && %s' % (self.data['basePath'], command)
|
|
|
|
res = ProcessUtilities.outputExecutioner(finalCommand, "root")
|
|
logging.writeToFile("compress file res %s"%res)
|
|
|
|
self.changeOwner(self.data['compressedFileName'])
|
|
|
|
## Update disk usage in background
|
|
command = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py UpdateDiskUsageForceDomain --domainName %s" % (domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def changePermissions(self):
|
|
try:
|
|
|
|
finalData = {}
|
|
finalData['status'] = 1
|
|
domainName = self.data['domainName']
|
|
website = Websites.objects.get(domain=domainName)
|
|
|
|
if self.data['recursive'] == 1:
|
|
command = 'chmod -R ' + self.data['newPermissions'] + ' ' + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['permissionsPath'])
|
|
else:
|
|
command = 'chmod ' + self.data['newPermissions'] + ' ' + self.returnPathEnclosed(
|
|
self.data['basePath'] + '/' + self.data['permissionsPath'])
|
|
|
|
ProcessUtilities.executioner(command, website.externalApp)
|
|
|
|
json_data = json.dumps(finalData)
|
|
return HttpResponse(json_data)
|
|
|
|
except BaseException as msg:
|
|
return self.ajaxPre(0, str(msg))
|
|
|
|
def fixPermissions(self, domainName):
|
|
|
|
website = Websites.objects.get(domain=domainName)
|
|
externalApp = website.externalApp
|
|
|
|
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
|
groupName = 'nobody'
|
|
else:
|
|
groupName = 'nogroup'
|
|
|
|
### symlink checks
|
|
|
|
command = 'ls -la /home/%s' % domainName
|
|
result = ProcessUtilities.outputExecutioner(command)
|
|
|
|
if result.find('->') > -1:
|
|
final_json = json.dumps(
|
|
{'status': 0, 'logstatus': 0,
|
|
'error_message': "Symlink attack."})
|
|
return HttpResponse(final_json)
|
|
|
|
command = 'chown %s:%s /home/%s' % (website.externalApp, website.externalApp, domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
### Sym link checks
|
|
|
|
command = 'ls -la /home/%s/public_html/' % domainName
|
|
result = ProcessUtilities.outputExecutioner(command)
|
|
|
|
if result.find('->') > -1:
|
|
final_json = json.dumps(
|
|
{'status': 0, 'logstatus': 0,
|
|
'error_message': "Symlink attack."})
|
|
return HttpResponse(final_json)
|
|
|
|
command = 'chown -R -P %s:%s /home/%s/public_html/*' % (externalApp, externalApp, domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
command = 'chown -R -P %s:%s /home/%s/public_html/.[^.]*' % (externalApp, externalApp, domainName)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
# command = "chown root:%s /home/" % (groupName) + domainName + "/logs"
|
|
# ProcessUtilities.popenExecutioner(command)
|
|
|
|
command = "find %s -type d -exec chmod 0755 {} \;" % ("/home/" + domainName + "/public_html")
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
command = "find %s -type f -exec chmod 0644 {} \;" % ("/home/" + domainName + "/public_html")
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
command = 'chown %s:%s /home/%s/public_html' % (externalApp, groupName, domainName)
|
|
ProcessUtilities.executioner(command)
|
|
|
|
command = 'chmod 750 /home/%s/public_html' % (domainName)
|
|
ProcessUtilities.executioner(command)
|
|
|
|
for childs in website.childdomains_set.all():
|
|
command = 'ls -la %s' % childs.path
|
|
result = ProcessUtilities.outputExecutioner(command)
|
|
|
|
if result.find('->') > -1:
|
|
final_json = json.dumps(
|
|
{'status': 0, 'logstatus': 0,
|
|
'error_message': "Symlink attack."})
|
|
return HttpResponse(final_json)
|
|
|
|
|
|
command = "find %s -type d -exec chmod 0755 {} \;" % (childs.path)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
command = "find %s -type f -exec chmod 0644 {} \;" % (childs.path)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
command = 'chown -R -P %s:%s %s/*' % (externalApp, externalApp, childs.path)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
command = 'chown -R -P %s:%s %s/.[^.]*' % (externalApp, externalApp, childs.path)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
command = 'chmod 755 %s' % (childs.path)
|
|
ProcessUtilities.popenExecutioner(command)
|
|
|
|
command = 'chown %s:%s %s' % (externalApp, groupName, childs.path)
|
|
ProcessUtilities.popenExecutioner(command)
|