server.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #!/usr/bin/python3
  2. import datetime
  3. import socket
  4. import codecs
  5. import os
  6. ## defining these functions to make code easier to read and edit
  7. def readfile(path, suppress_filenotfound_error):
  8. try:
  9. return codecs.open(path, "r", encoding = "UTF-8").read()
  10. except FileNotFoundError:
  11. if not suppress_filenotfound_error:
  12. print("No such file: {}".format(path))
  13. return False
  14. except Exception as e:
  15. print("Failed to read file {} ({})".format(path, e))
  16. return False
  17. def get_current_date():
  18. return datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S-%f")
  19. def write_message(mail_from, rcpt_to, data, addr):
  20. ## also preventing it from overwriting existing files
  21. current_antidup_number = 0
  22. current_time = get_current_date()
  23. while True:
  24. try:
  25. if current_antidup_number == 0:
  26. open("messages/{}_{}.txt".format(current_time, addr), "r")
  27. else:
  28. open("messages/{}_{}-{}.txt".format(current_time, addr, current_antidup_number), "r")
  29. except FileNotFoundError:
  30. break
  31. if current_antidup_number == 0:
  32. filename = "messages/{}_{}.txt".format(current_time, addr)
  33. else:
  34. filename = "messages/{}_{}-{}.txt".format(current_time, addr, current_antidup_number)
  35. f = open(filename, "w", encoding = "UTF-8")
  36. f.write("FROM: {}\r\nTO: {}\r\nDATA:\r\n{}".format(mail_from, rcpt_to, data))
  37. f.close()
  38. return filename
  39. def intify(x):
  40. try:
  41. return int(x)
  42. except Exception as e:
  43. print("Failed to parse integer from {} ({})".format(x, e))
  44. return False
  45. ## creating missing directories
  46. required_dirs = ["config", "logs", "messages"]
  47. for directory in required_dirs:
  48. try:
  49. os.listdir(directory)
  50. except FileNotFoundError:
  51. print("[NOTICE] Creating {}/ folder".format(directory))
  52. os.mkdir(directory)
  53. except Exception as e:
  54. print("[ERROR] Can't access {}/ directory, it might cause following errors (current error: {})".format(directory, e))
  55. ## starting the server
  56. listening_port = readfile("config/port", True)
  57. if listening_port == False:
  58. listening_port = 2525
  59. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  60. s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  61. s.bind( ("", intify(listening_port)) )
  62. s.listen()
  63. while True:
  64. try:
  65. conn, addr = s.accept()
  66. conn.settimeout(1800)
  67. print("[LOG] Accepted connection from {}:{}".format(addr[0], addr[1]))
  68. conn.send("220 {}\r\n".format( readfile("config/domain", True) ).encode("UTF-8"))
  69. if readfile("config/debug", True) != False:
  70. print("[DEBUG] responce given")
  71. connection_alive = True
  72. data_buffer = bytes()
  73. receiving_mail_body = False
  74. reset_buffer = False
  75. mail_from = ""
  76. rcpt_to = ""
  77. data = ""
  78. while connection_alive:
  79. while True:
  80. if reset_buffer:
  81. data_buffer = bytes()
  82. reset_buffer = False
  83. new_data = conn.recv(1024)
  84. if readfile("config/debug", True) != False:
  85. print("[DEBUG] New_data: {}".format(new_data))
  86. if len(new_data) == 0:
  87. print("[LOG] Connection {}:{} closed early".format(addr[0], addr[1]))
  88. connection_alive = False
  89. break
  90. data_buffer += new_data
  91. if not receiving_mail_body:
  92. if data_buffer[-2:] == "\r\n".encode("UTF-8"):
  93. reset_buffer = True
  94. break
  95. else:
  96. if data_buffer[-5:] == "\r\n.\r\n".encode("UTF-8"):
  97. reset_buffer = True
  98. data = data_buffer[:-5].decode("UTF-8")
  99. fn = write_message(mail_from, rcpt_to, data, addr[0] + "_" + str(addr[1]))
  100. conn.send("250 OK: saved as {}\r\n".format(fn).encode("UTF-8"))
  101. receiving_mail_body = False
  102. break
  103. command = data_buffer.decode("UTF-8")
  104. if not receiving_mail_body:
  105. if command[:4] == "HELO":
  106. conn.send("250 {}\r\n".format( readfile("config/domain", True) ).encode("UTF-8"))
  107. elif command[:4] == "EHLO":
  108. conn.send("250 {}\r\n".format( readfile("config/domain", True) ).encode("UTF-8"))
  109. elif command[:10] == "MAIL FROM:":
  110. mail_from = command[10:-2]
  111. conn.send("250 OK\r\n".encode("UTF-8"))
  112. elif command[:8] == "RCPT TO:":
  113. rcpt_to = command[8:-2]
  114. conn.send("250 OK\r\n".encode("UTF-8"))
  115. elif command[:4] == "DATA":
  116. receiving_mail_body = True
  117. conn.send("354\r\n".encode("UTF-8"))
  118. elif command[:4] == "QUIT":
  119. conn.send("221 Bye\r\n".encode("UTF-8"))
  120. conn.close()
  121. connection_alive = False
  122. print("[LOG] Connection {}:{} closed".format(addr[0], addr[1]))
  123. continue
  124. except KeyboardInterrupt:
  125. print("\rStopping the server...")
  126. break
  127. except Exception as e:
  128. print("[ERROR] Unexpected error on connection {}:{} ({})".format(addr[0], addr[1], e))
  129. conn.close()