121 lines
2.7 KiB
Python
121 lines
2.7 KiB
Python
|
from socket import *
|
||
|
from ftp_utils import *
|
||
|
from enum import Enum
|
||
|
from time import sleep
|
||
|
from pathlib import Path
|
||
|
import os
|
||
|
import sys
|
||
|
import traceback
|
||
|
|
||
|
HOST = '127.0.0.1'
|
||
|
|
||
|
PORT = 12000
|
||
|
if len(sys.argv) > 1:
|
||
|
PORT = int(sys.argv[1])
|
||
|
|
||
|
DEBUG = True
|
||
|
MAX_FILE_SIZE = 65536 # 64 KiB
|
||
|
|
||
|
# set up the tcp sockets
|
||
|
sock = socket(AF_INET, SOCK_STREAM)
|
||
|
conn = None # for now...
|
||
|
|
||
|
if DEBUG:
|
||
|
# Allows for quicker debugging at risk of stray
|
||
|
# packets messing with the byte stream
|
||
|
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||
|
|
||
|
sock.bind((HOST, PORT))
|
||
|
sock.listen()
|
||
|
|
||
|
def ftp_GET(*args):
|
||
|
global conn
|
||
|
file_name = get_argument(args, "file name", 0)
|
||
|
|
||
|
with open(os.path.join("./server", file_name), "rb") as fetched_file:
|
||
|
file_bytes = fetched_file.read()
|
||
|
conn.sendall(file_bytes)
|
||
|
|
||
|
def ftp_PUT(*args):
|
||
|
file_name = get_argument(args, "file name", 0)
|
||
|
size = int(get_argument(args, "file size", 1))
|
||
|
|
||
|
if size > MAX_FILE_SIZE:
|
||
|
throw_error(conn, 3884, f"File exceeds maximum file size: {MAX_FILE_SIZE}")
|
||
|
return
|
||
|
|
||
|
conn.sendall("OK".encode("utf-8"))
|
||
|
file_bytes = conn.recv(size)
|
||
|
|
||
|
parent_path = os.path.join("./server", str(Path(file_name).parent))
|
||
|
|
||
|
if not os.path.exists(parent_path):
|
||
|
os.makedirs(parent_path)
|
||
|
|
||
|
with open(os.path.join("server", file_name), "wb") as new_file:
|
||
|
new_file.write(file_bytes)
|
||
|
|
||
|
print(f"File '{file_name}' uploaded")
|
||
|
|
||
|
def ftp_CLOSE(*args):
|
||
|
global conn
|
||
|
send_message(conn, "bye<3")
|
||
|
conn.close()
|
||
|
conn, addr = sock.accept()
|
||
|
|
||
|
def ftp_SIZE(*args):
|
||
|
global conn
|
||
|
file_name = get_argument(args, "file name", 0)
|
||
|
|
||
|
try:
|
||
|
send_message(conn, str(os.path.getsize(os.path.join("./server", file_name))))
|
||
|
except FileNotFoundError:
|
||
|
raise FTPError(f"File '{file_name}' not found", 9283)
|
||
|
|
||
|
COMMANDS = {
|
||
|
"GET": ftp_GET,
|
||
|
"PUT": ftp_PUT,
|
||
|
"CLOSE": ftp_CLOSE,
|
||
|
"SIZE": ftp_SIZE,
|
||
|
}
|
||
|
|
||
|
def throw_error(conn, code, msg):
|
||
|
conn.sendall(f"Error {code}: {msg}".encode("utf-8"))
|
||
|
|
||
|
def send_message(conn, msg):
|
||
|
conn.sendall(msg.encode("utf-8"))
|
||
|
|
||
|
# listen for a connection
|
||
|
conn, addr = sock.accept()
|
||
|
print("Connected to " , addr)
|
||
|
|
||
|
try:
|
||
|
while (True):
|
||
|
data = conn.recv(1024).decode("utf-8")
|
||
|
|
||
|
command_name, arguments = parse_ftp_command(data)
|
||
|
|
||
|
# Let's allow for case insensitivity in the command name
|
||
|
command_name = command_name.upper()
|
||
|
|
||
|
try:
|
||
|
result = COMMANDS[command_name](*arguments)
|
||
|
except KeyError:
|
||
|
throw_error(conn, 2, f"Unknown command '{command_name}'")
|
||
|
continue
|
||
|
except FTPError as err:
|
||
|
throw_error(conn, err.code, err.message)
|
||
|
except Exception as err:
|
||
|
print(f"Error: {err}")
|
||
|
traceback.print_exc()
|
||
|
throw_error(conn, -1, f"Unknown error while executing {command_name}")
|
||
|
except BrokenPipeError:
|
||
|
print("Broken Pipe. Awaitng new connections...")
|
||
|
conn, addr = sock.accept()
|
||
|
except Exception as e:
|
||
|
print(f"Error: {e}")
|
||
|
traceback.print_exc()
|
||
|
finally:
|
||
|
conn.close()
|
||
|
sock.close()
|