123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- #!/usr/bin/python3
- import datetime
- import socket
- import codecs
- import os
- ## defining these functions to make code easier to read and edit
- def readfile(path, suppress_filenotfound_error):
- try:
- return codecs.open(path, "r", encoding = "UTF-8").read()
- except FileNotFoundError:
- if not suppress_filenotfound_error:
- print("No such file: {}".format(path))
- return False
- except Exception as e:
- print("Failed to read file {} ({})".format(path, e))
- return False
- def get_current_date():
- return datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S-%f")
- def write_message(mail_from, rcpt_to, data, addr):
- ## also preventing it from overwriting existing files
- current_antidup_number = 0
- current_time = get_current_date()
- while True:
- try:
- if current_antidup_number == 0:
- open("messages/{}_{}.txt".format(current_time, addr), "r")
- else:
- open("messages/{}_{}-{}.txt".format(current_time, addr, current_antidup_number), "r")
- except FileNotFoundError:
- break
- if current_antidup_number == 0:
- filename = "messages/{}_{}.txt".format(current_time, addr)
- else:
- filename = "messages/{}_{}-{}.txt".format(current_time, addr, current_antidup_number)
- f = open(filename, "w", encoding = "UTF-8")
- f.write("FROM: {}\r\nTO: {}\r\nDATA:\r\n{}".format(mail_from, rcpt_to, data))
- f.close()
- return filename
- def intify(x):
- try:
- return int(x)
- except Exception as e:
- print("Failed to parse integer from {} ({})".format(x, e))
- return False
- ## creating missing directories
- required_dirs = ["config", "logs", "messages"]
- for directory in required_dirs:
- try:
- os.listdir(directory)
- except FileNotFoundError:
- print("[NOTICE] Creating {}/ folder".format(directory))
- os.mkdir(directory)
- except Exception as e:
- print("[ERROR] Can't access {}/ directory, it might cause following errors (current error: {})".format(directory, e))
- ## starting the server
- listening_port = readfile("config/port", True)
- if listening_port == False:
- listening_port = 2525
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind( ("", intify(listening_port)) )
- s.listen()
- while True:
- try:
- conn, addr = s.accept()
- conn.settimeout(1800)
- print("[LOG] Accepted connection from {}:{}".format(addr[0], addr[1]))
- conn.send("220 {}\r\n".format( readfile("config/domain", True) ).encode("UTF-8"))
- if readfile("config/debug", True) != False:
- print("[DEBUG] responce given")
- connection_alive = True
- data_buffer = bytes()
- receiving_mail_body = False
- reset_buffer = False
- mail_from = ""
- rcpt_to = ""
- data = ""
- while connection_alive:
- while True:
- if reset_buffer:
- data_buffer = bytes()
- reset_buffer = False
- new_data = conn.recv(1024)
- if readfile("config/debug", True) != False:
- print("[DEBUG] New_data: {}".format(new_data))
- if len(new_data) == 0:
- print("[LOG] Connection {}:{} closed early".format(addr[0], addr[1]))
- connection_alive = False
- break
- data_buffer += new_data
- if not receiving_mail_body:
- if data_buffer[-2:] == "\r\n".encode("UTF-8"):
- reset_buffer = True
- break
- else:
- if data_buffer[-5:] == "\r\n.\r\n".encode("UTF-8"):
- reset_buffer = True
- data = data_buffer[:-5].decode("UTF-8")
- fn = write_message(mail_from, rcpt_to, data, addr[0] + "_" + str(addr[1]))
- conn.send("250 OK: saved as {}\r\n".format(fn).encode("UTF-8"))
- receiving_mail_body = False
- break
- command = data_buffer.decode("UTF-8")
- if not receiving_mail_body:
- if command[:4] == "HELO":
- conn.send("250 {}\r\n".format( readfile("config/domain", True) ).encode("UTF-8"))
- elif command[:4] == "EHLO":
- conn.send("250 {}\r\n".format( readfile("config/domain", True) ).encode("UTF-8"))
- elif command[:10] == "MAIL FROM:":
- mail_from = command[10:-2]
- conn.send("250 OK\r\n".encode("UTF-8"))
- elif command[:8] == "RCPT TO:":
- rcpt_to = command[8:-2]
- conn.send("250 OK\r\n".encode("UTF-8"))
- elif command[:4] == "DATA":
- receiving_mail_body = True
- conn.send("354\r\n".encode("UTF-8"))
- elif command[:4] == "QUIT":
- conn.send("221 Bye\r\n".encode("UTF-8"))
- conn.close()
- connection_alive = False
- print("[LOG] Connection {}:{} closed".format(addr[0], addr[1]))
- continue
- except KeyboardInterrupt:
- print("\rStopping the server...")
- break
- except Exception as e:
- print("[ERROR] Unexpected error on connection {}:{} ({})".format(addr[0], addr[1], e))
- conn.close()
|