This repository has been archived on 2023-11-03. You can view files and clone it, but cannot push or open issues or pull requests.
ACMS/Server/fileCommands.py

600 lines
17 KiB
Python

#!/usr/bin/env python3.5
# -*- coding: utf-8 -*-
"""
A single file for each command file we implemented.
"""
import os
import shutil
import datetime
import threading
import subprocess
from socketCommands import sendData
from CommandException import CommandException
__authors__ = "HorlogeSkynet, Tatiyk, CaumartinYann"
__copyright__ = "Copyright 2017, ACMS"
__license__ = "GPLv3"
__status__ = "Development"
__date__ = "03/03/2017"
# Default path for Data
DATA_PATH = 'Server/Data/'
class sendOutput(threading.Thread):
def __init__(self, sock, pipeOutput):
threading.Thread.__init__(self)
self.sock = sock
self.keepGoing = True
self.pipeOutput = pipeOutput
def run(self):
while True:
c = ''
while not c:
if not self.keepGoing:
return
else:
c = self.pipeOutput.read()
sendData(self.sock, c.decode())
def stop(self):
self.keepGoing = False
self.join()
class recvInput(threading.Thread):
def __init__(self, sock, pipeInput):
threading.Thread.__init__(self)
self.sock = sock
self.keepGoing = True
self.pipeInput = pipeInput
def run(self):
while True:
c = self.sock.recv(1)
if not self.keepGoing:
break
else:
self.pipeInput.write(c)
def stop(self):
self.keepGoing = False
self.join()
def removeFile(command, client, acl):
def updateAcl(path, acl):
for file in os.listdir(path):
if os.path.isdir(path + file):
acl.deleteFile(client.username, path + file + '/')
updateAcl(path + file + '/', acl)
else:
acl.deleteFile(client.username, path + file)
nbArgs = len(command)
if nbArgs == 2:
path = interpretPath(command[1], client)
if os.path.exists(path):
with client.mutex:
if os.path.isdir(path):
if acl.isAllowedToOn(client.username, 'write', path + '/'):
try:
os.rmdir(path)
acl.deleteFile(client.username, path + '/')
except:
raise CommandException("The directory is not empty. Use \'-r\' option.")
else:
raise CommandException("You\'re not allowed to perform this operation.")
else:
if acl.isAllowedToOn(client.username, 'write', path):
try:
os.remove(path)
acl.deleteFile(client.username, path)
except:
raise CommandException("The file couldn't be removed")
else:
raise CommandException("You\'re not allowed to perform this operation.")
else:
raise CommandException("Unknown path.")
elif nbArgs == 3:
if command.count("-r") == 1:
if command[1] == "-r":
command[1], command[2] = command[2], command[1]
path = interpretPath(command[1], client)
if os.path.exists(path):
with client.mutex:
if acl.isAllowedToOn(client.username, 'write', path + '/'):
if os.path.isdir(path):
try:
acl.deleteFile(client.username, path + '/')
updateAcl(path + '/', acl)
shutil.rmtree(path, ignore_errors=True)
except:
raise CommandException("The directory \"" + command[1] + "\" couldn\'t be removed.")
else:
try:
os.remove(path)
acl.deleteFile(client.username, path)
except:
raise CommandException("Omitted option \'-r\', but \"" + command[1] + "\" couldn\'t be removed.")
else:
raise CommandException("You\'re not allowed to perform this operation.")
else:
raise CommandException("Unknown path.")
else:
raise CommandException("Unknown arguments.")
else:
raise CommandException("Wrong number of arguments.")
def moveFile(command, client, acl):
def updateAclDelete(source, acl):
for file in os.listdir(source):
if os.path.isdir(source + file):
acl.deleteFile(client.username, source + file + '/')
updateAclDelete(source + file + '/', acl)
else:
acl.deleteFile(client.username, source + file)
def updateAclAdd(destination, acl):
for file in os.listdir(destination):
if os.path.isdir(destination + file):
acl.addFile(client.username, destination + file + '/')
updateAclAdd(destination + file + '/', acl)
else:
acl.addFile(client.username, destination + file)
nbArgs = len(command)
if nbArgs == 3:
if command[1].endswith('/'):
command[1] = command[1].rpartition('/')[0]
source = interpretPath(command[1], client)
destination = interpretPath(command[2], client)
if not os.path.exists(destination):
with client.mutex:
if os.path.isdir(source):
try:
if acl.isAllowedToOn(client.username, 'write', source.rpartition('/')[0] + '/') and acl.isAllowedToOn(client.username, 'write', destination.rpartition('/')[0] + '/'):
acl.deleteFile(client.username, source + '/')
acl.addFile(client.username, destination + '/')
updateAclDelete(source + '/', acl)
shutil.move(source, destination)
updateAclAdd(destination + '/', acl)
else:
raise CommandException("You\'re not allowed to perform this operation.")
except:
raise CommandException("An error occurred while moving your directory...")
else:
try:
shutil.move(source, destination)
acl.addfile(client.username, destination)
acl.deleteFile(client.username, source)
except:
raise CommandException("An error occurred while moving your file...")
# Si le deuxième argument est un dossier qui existe, le mv dedans ...
elif os.path.isdir(destination):
with client.mutex:
if os.path.isdir(source):
try:
if acl.isAllowedToOn(client.username, 'write', source.rpartition('/')[0] + '/') and acl.isAllowedToOn(client.username, 'write', destination.rpartition('/')[0] + '/'):
acl.deleteFile(client.username, source + '/')
acl.addFile(client.username, destination + '/' + command[1] + '/')
updateAclDelete(source + '/', acl)
shutil.move(source, destination + '/' + command[1] + '/')
updateAclAdd(destination + '/' + command[1] + '/', acl)
else:
raise CommandException("You\'re not allowed to perform this operation.")
except:
raise CommandException("An error occurred while moving your directory...")
else:
try:
acl.deleteFile(client.username, source)
acl.addFile(client.username, destination + '/' + command[1])
shutil.move(source, destination + '/' + command[1])
except:
raise CommandException("An error occurred while moving your file...")
else:
raise CommandException("The destination specified already exist !")
else:
raise CommandException("Wrong number of arguments.")
def makeDirectory(command, client, acl):
def updateAcl(pathKnown, directories, acl):
for directory in directories:
acl.addFile(client.username, pathKnown + directory + '/')
pathKnown = pathKnown + directory + '/'
nbArgs = len(command)
if nbArgs == 3:
pathKnown = ''
if command.count("-p") == 1:
if command[2] == "-p":
command[2] = command[1]
path = interpretPath(command[2], client)
directories = path.split('/')
while os.path.isdir(pathKnown + directories[0]):
pathKnown = pathKnown + directories[0] + '/'
directories.remove(directories[0])
try:
if acl.isAllowedToOn(client.username, 'write', pathKnown):
with client.mutex:
os.makedirs(path)
updateAcl(pathKnown, directories, acl)
else:
raise CommandException("You\'re not allowed to perform this operation.")
except:
raise CommandException("Permission denied.")
elif nbArgs == 2:
try:
path = interpretPath(command[1], client)
if acl.isAllowedToOn(client.username, 'write', path.rpartition('/')[0] + '/'):
with client.mutex:
os.mkdir(path)
acl.addFile(client.username, path + '/')
else:
raise CommandException("You\'re not allowed to perform this operation.")
except:
raise CommandException("The directory couldn\'t be created.")
else:
raise CommandException("Wrong number of arguments.")
def changeDirectory(command, client, acl):
nbArgs = len(command)
if nbArgs == 1:
path = interpretPath('~', client)
if not path.endswith('/'):
path += '/'
if not os.path.exists(path):
raise CommandException("This directory does not exist.")
else:
if acl.isAllowedToOn(client.username, 'execute', path):
client.currentWorkingDir = path
else:
raise CommandException("You\'re not allowed to perform this operation.")
elif nbArgs == 2:
path = interpretPath(command[1], client)
if not path.endswith('/'):
path += '/'
if not os.path.exists(path):
raise CommandException("This directory does not exist.")
else:
if acl.isAllowedToOn(client.username, 'execute', path):
client.currentWorkingDir = path
else:
raise CommandException("You\'re not allowed to perform this operation.")
else:
raise CommandException("Wrong number of arguments.")
def listFiles(command, client, acl):
nbArgs = len(command)
if nbArgs == 1 or (nbArgs == 2 and command.count("-l") == 0):
path = None
files = None
if nbArgs == 2:
path = interpretPath(command[1], client)
if not path.endswith('/'):
path += '/'
else:
path = client.currentWorkingDir
if not os.path.exists(path) or not os.path.isdir(path):
raise CommandException("Unknown path (or is it really a directory ?).")
else:
message = ''
if acl.isAllowedToOn(client.username, 'read', path):
files = os.listdir(path)
if len(files) == 0:
message = "This directory is empty."
else:
message = "\n".join(files)
return message
else:
raise CommandException("You\'re not allowed to perform this operation.")
elif (nbArgs == 2 or nbArgs == 3) and command.count("-l") == 1:
path = client.currentWorkingDir
if nbArgs == 3:
if command[1] == "-l":
command[1], command[2] = command[2], command[1]
path = interpretPath(command[1], client)
if not os.path.exists(path) or not os.path.isdir(path):
raise CommandException("Unknown path (or is it really a directory ?).")
message = ''
if acl.isAllowedToOn(client.username, 'read', path):
files = os.listdir(path)
if len(files) == 0:
message = "This directory is empty."
else:
tailleMax = max([len(str(os.path.getsize(path + file))) for file in files])
for file in files:
if os.path.isdir(path + file):
message += "d "
elif os.path.isfile(path + file):
message += "- "
elif os.path.isabs(path + file):
message += "a "
elif os.path.islink(path + file):
message += "l "
else:
message += "? "
message += ' ' * (tailleMax - len(str(os.path.getsize(path + file))))
message += str(os.path.getsize(path + file)) + ' ' + str(datetime.datetime.fromtimestamp(os.path.getmtime(path + file))) + ' ' + file + '\n'
return message
else:
raise CommandException("You\re not allowed to perform this operation.")
else:
raise CommandException("Wrong number of (or invalid) arguments.")
def copyFile(command, client, acl):
def updateAcl(destination, acl):
for file in os.listdir(destination):
if os.path.isdir(destination + file):
acl.addFile(client.username, destination + file + '/')
updateAcl(destination + file + '/', acl)
else:
acl.addFile(client.username, destination + file)
nbArgs = len(command)
if nbArgs == 3:
source = interpretPath(command[1], client)
destination = interpretPath(command[2], client)
if not os.path.exists(destination):
if not os.path.isdir(source):
with client.mutex:
try:
if acl.isAllowedToOn(client.username, 'read', source) and acl.isAllowedToOn(client.username, 'write', destination.rpartition('/')[0]):
shutil.copy(source, destination)
acl.addFile(client.username, destination)
else:
raise CommandException("You\'re not allowed to perform this operation.")
except:
raise CommandException("An error occurred while copying your file...")
else:
raise CommandException("You want to copy a directory ? Use \'-r\' option.")
else:
raise CommandException("The destination specified already exist !")
elif nbArgs == 4:
if command[2] == "-r":
command[2], command[1] = command[1], command[2]
elif command[3] == "-r":
command[3], command[1] = command[1], command[3]
if command[2].endswith('/'):
command[2] = command[2].rpartition('/')[0]
source = interpretPath(command[2], client)
destination = interpretPath(command[3], client)
if not os.path.exists(destination):
with client.mutex:
if os.path.isdir(source):
try:
if acl.isAllowedToOn(client.username, 'read', source + '/') and acl.isAllowedToOn(client.username, 'write', destination.rpartition('/')[0] + '/'):
shutil.copytree(source, destination)
acl.addFile(client.username, destination + '/')
updateAcl(destination + '/', acl)
else:
raise CommandException("You\'re not allowed to perform this operation.")
except:
raise CommandException("An error occurred while copying your directory...")
else:
try:
if acl.isAllowedToOn(client.username, 'read', source) and acl.isAllowedToOn(client.username, 'write', destination.rpartition('/')[0]):
shutil.copy(source, destination)
acl.addFile(client.username, destination)
except:
raise CommandException("An error occurred while copying your file...")
# Si le deuxieme argument est un dossier qui exitse.
elif os.path.isdir(destination):
with client.mutex:
if os.path.isdir(source):
try:
if acl.isAllowedToOn(client.username, 'read', source + '/') and acl.isAllowedToOn(client.username, 'write', destination.rpartition('/')[0] + '/'):
shutil.copytree(source, destination + '/' + command[2])
acl.addFile(client.username, destination + '/' + command[2] + '/')
updateAcl(destination + '/' + command[2] + '/', acl)
else:
raise CommandException("You\'re not allowed to perform this operation.")
except:
raise CommandException("An error occurred while copying your directory...")
else:
try:
if acl.isAllowedToOn(client.username, 'read', source) and acl.isAllowedToOn(client.username, 'write', destination.rpartition('/')[0]):
shutil.copy(source, destination)
acl.addFile(client.username, destination + '/' + command[2])
except:
raise CommandException("An error occurred while copying your file...")
else:
raise CommandException("The destination specified already exist !")
else:
raise CommandException("Wrong number of arguments.")
def textEditor(command, client, acl, readOnly=False):
if len(command) != 2:
raise CommandException("Wrong number of arguments.")
if command[0] == 'vim':
command.append('-Z') # Do not allow VIM to run shell commands !
if readOnly:
command.append('-R')
else:
pass # Case to handle (Hello Windows !)
path = interpretPath(command[1], client)
command[1] = os.getcwd() + '/' + path
wasPresent = None
if readOnly:
if not os.path.exists(path):
raise CommandException("This file does not exist.")
if not acl.isAllowedToOn(client.username, 'read', path):
raise CommandException("You\'re not allowed to perform this operation.")
else:
if not os.path.exists(path):
if not acl.isAllowedToOn(client.username, 'write', path.rpartition('/')[0] + '/'):
raise CommandException("You\'re not allowed to perform this operation.")
else:
wasPresent = False
else:
if not acl.isAllowedToOn(client.username, 'write', path):
raise CommandException("You\'re not allowed to perform this operation.")
else:
wasPresent = True
# Asks the client to change its mode to "STREAMS REDIRECTION"
sendData(client.sock, 'REDIRECT')
# Runs the local text editor and handles the redirections
p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=0)
# Let's set the output subprocess' pipe to NO_BLOCK mode (otherwise the reading would be biased)
os.set_blocking(p.stdout.fileno(), False)
# Two external threads in order to handle pipeInputut to and (output from) VIM
outputThread = sendOutput(client.sock, p.stdout)
inputThread = recvInput(client.sock, p.stdin)
# Let's start them !
outputThread.start()
inputThread.start()
# Let's wait for termination of the editor
p.wait()
# Let's check if the file has appeared or not
if not readOnly and not wasPresent and os.path.exists(path):
acl.addFile(client.username, path)
# The editor has stopped, let's demand to the client to re-change to its "normal" mode with a token that could be sent only in this case
sendData(client.sock, "VIM_STOP_REDIRECT_ACMS_2017_TOKEN:$}a*M-2i5<9Ù3(+$")
# Once the process has stopped, let's kill and wait the threads termination
outputThread.stop()
inputThread.stop()
#################
# Miscellaneous #
#################
def interpretPath(path, client):
# Absolute and relative paths are managed here
if path.startswith('/'):
path = path.split('/', 1)[1]
path = DATA_PATH + path
elif path.startswith('~'):
path = path.split('~', 1)[1]
path = client.HOME_DIR + path
else:
path = client.currentWorkingDir + path
# '//' (bad thing !!) is managed here
path.replace('//', '/')
# '..' is managed here
while path.count('/..') != 0 or path.count('../') != 0:
temp = path.partition('..')
path = temp[0].rsplit('/', 2)[0] + temp[2]
if path.endswith('/'):
path = path.rpartition('/')[0]
# If the user typed too many times '..' in, let's put him in the root directory
if not path.startswith(DATA_PATH):
path = DATA_PATH
return path