simple-ftp-server/server.py

121 lines
2.7 KiB
Python
Executable File

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