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()