import email import os import socket import threading from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from queue import Queue import pandas as pd from utils.encryption import encrypt_data, decrypt_data # Server configuration SERVER_HOST = '0.0.0.0' SERVER_PORT = 1234 saveMail_directory = "FlowSteering/ApplicationCode/EmailServer/EmailServerMailDatabase" # Change this to the directory where you want to save the emails inbox for each user message_queue = Queue() default_image = 'FlowSteering/assets/PerturbatedImages/DjiPerturbClassForward.png' # Server configuration def receive_complete_data( client_socket): # This function is used to receive the complete data from the client, adjust the parameters as needed based on your network conditions received_data = b"" count = 0 client_socket.settimeout(3.0) try: while True: chunk = client_socket.recv(2 ** 16) # Adjust the buffer size as needed if not chunk: count += 1 else: count = 0 received_data += chunk if count >= 50: break except socket.timeout as e: print('timeout') print(e) pass return received_data def handle_messages(): # This function is used to handle the messages in the queue and process them accordingly based on the command received from the client (e.g., SEND_EMAIL, CHECK_INBOX) while True: if not message_queue.empty(): print('______________________________________________________________') data, client_socket, client_address = message_queue.get() msg = email.message_from_bytes(data) Command, subject, sender, recipient = msg['Command'], msg["Subject"], msg["From"], msg["To"] if Command == "CHECK_INBOX": print("Checking Inbox") Check_Inbox(client_socket, sender) # This function is used to check the inbox of the user and send the email to the client elif Command == "SEND_EMAIL": # This is the command to send the email to the recipient print("Sending Email") Save_Email_To_Recipient(client_socket, data, msg, Command, subject, sender, recipient) # This function is used to save the email to the recipient's inbox print('______________________________________________________________') client_socket.close() def Save_Email_To_Recipient(client_socket, data, msg, requests, subject, sender, recipient): # This function is used to save the email to the recipient's inbox recipient_directory = f"{saveMail_directory}/{recipient}" # This is the directory where the emails will be saved os.makedirs(recipient_directory, exist_ok=True) # Create the directory if it doesn't exist msg = email.message_from_bytes(data) if msg.is_multipart(): for part in msg.get_payload(): if part.get_content_type() == "text/plain": body = part.get_payload() else: print(msg.get_payload()) for part in msg.walk(): if part.get_content_maintype() == "multipart": continue if part.get("Content-Disposition") is None: continue # Get the filename filename = part.get_filename() # split the filename by "\" and take the last part of it #filename = filename.split("\\")[-1] filename = filename.split("/")[-1] # Save the image file with open(os.path.join(recipient_directory, filename), "wb") as f: f.write(part.get_payload(decode=True)) print(f"From: {sender}") print(f"To: {recipient}") print(f"Subject: {subject}") print(f"Attachment filename: {filename}") print(f' Text body: {body}') filepath = str(f"{recipient_directory}/{filename}") # Encrypt email data encrypted_body, body_key = encrypt_data(body) encrypted_filepath, filepath_key = encrypt_data(filepath) email_data = [[sender, recipient, subject, encrypted_body, encrypted_filepath, body_key, filepath_key]] MyColumns = ['Sender', 'Recipient', 'Subject', 'Body', 'FilePath', 'BodyKey', 'FilePathKey'] if not os.path.isfile(f"{recipient_directory}/{recipient}_received_emails.csv") or ( os.stat(f"{recipient_directory}/{recipient}_received_emails.csv").st_size == 0): # If the file doesn't exist, then create the file and save the email to the file df = pd.DataFrame(email_data, columns=MyColumns) df.to_csv(f"{recipient_directory}/{recipient}_received_emails.csv", mode='w', header=True, index=False) # Save the email to the recipient's inbox df.to_csv(f"{recipient_directory}/{recipient}_received_emailsHistory.csv", mode='w', header=True, index=False) # Save the email to the recipient's inbox history else: # If the file already exists, then append the email to the file df = pd.read_csv(f"{recipient_directory}/{recipient}_received_emails.csv") # Read the csv file of the recipient new_row_df = pd.DataFrame(email_data, columns=df.columns) df = pd.concat([df, new_row_df], ignore_index=True) df.to_csv(f"{recipient_directory}/{recipient}_received_emails.csv", mode='w', header=True, index=False) df = pd.read_csv(f"{recipient_directory}/{recipient}_received_emailsHistory.csv") df = pd.concat([df, new_row_df], ignore_index=True) df.to_csv(f"{recipient_directory}/{recipient}_received_emailsHistory.csv", mode='w', header=True, index=False) # write back to the sender that the email was sent client_socket.sendall("Email Sent".encode('utf-8')) def Check_Inbox(client_socket, sender): # This function is used to check the inbox of the user and send the email to the client print(f' A request ot check the inbox email from: {sender}') sender_directory = f"{saveMail_directory}/{sender}" os.makedirs(sender_directory, exist_ok=True) if (not os.path.isfile(f"{sender_directory}/{sender}_received_emails.csv")) or ( os.stat(f"{sender_directory}/{sender}_received_emails.csv").st_size == 0): client_socket.sendall("No Emails".encode('utf-8')) return df = pd.read_csv(f"{sender_directory}/{sender}_received_emails.csv") rows = df.shape[0] print(f'found {rows} emails in the inbox of {sender}') if rows == 0: # If there are no emails in the inbox, then send "No Emails" to the client client_socket.sendall("No Emails".encode('utf-8')) return else: # If there are emails in the inbox, then send the email to the client # take the last row of the csv file header_columns = df.columns last_row = df.tail(1) msg = MIMEMultipart() msg["Command"] = "SEND_EMAIL" msg["From"] = last_row['Sender'].values[0] msg["To"] = last_row['Recipient'].values[0] msg["Subject"] = last_row['Subject'].values[0] # Decrypt email data decrypted_body = decrypt_data(last_row['Body'].values[0], last_row['BodyKey'].values[0]) decrypted_filepath = decrypt_data(last_row['FilePath'].values[0], last_row['FilePathKey'].values[0]) msg.attach(MIMEText(decrypted_body, "plain")) filename = decrypted_filepath with open(filename, "rb") as f: try: #We faced some network errors resulting in images being sent partially black. To address this issue, we implemented a try-except block to handle such occurrences. Now, if an image fails to send correctly, a default image is sent for that experiment. img = MIMEImage(f.read()) img.add_header("Content-Disposition", "attachment", filename=filename) msg.attach(img) except: print('network error, sending default image instead of the original image') with open(default_image,"rb") as f: img = MIMEImage(f.read()) img.add_header("Content-Disposition", "attachment", filename=filename) msg.attach(img) message = msg.as_bytes() # send the message to the client df.drop(df.tail(1).index, inplace=True) df.to_csv(f"{sender_directory}/{sender}_received_emails.csv", mode='w', header=True, index=False) client_socket.sendall(message) return def start_server(): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((SERVER_HOST, SERVER_PORT)) server_socket.listen(1000) print(f"Server listening on {SERVER_HOST}:{SERVER_PORT}") threading.Thread(target=handle_messages, daemon=True).start() while True: client_socket, client_address = server_socket.accept() print(len(message_queue.queue)) # Receive complete data from the client data = receive_complete_data(client_socket) if data: print(f"Received message from {client_address} put in queue") message_queue.put((data, client_socket, client_address)) if __name__ == '__main__': os.makedirs(saveMail_directory, exist_ok=True) start_server()