600 lines
17 KiB
Python
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
|