From 492b384a9255492bccf6c7920b2a17180638446d Mon Sep 17 00:00:00 2001 From: PC-Admin Date: Mon, 24 Jul 2023 02:59:49 +0800 Subject: [PATCH] add generic email send function for automated reporting, hardcode matrix.org abuse email because... well I wanna stay in their good books lol --- moderation_tool.py | 4 ++- report_commands.py | 52 ++++++++++++++++++++++++++++++++++++++ server_commands.py | 41 +++++++++++++++--------------- test_data/evil_clown.jpeg | Bin 0 -> 10212 bytes 4 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 test_data/evil_clown.jpeg diff --git a/moderation_tool.py b/moderation_tool.py index 290d58f..40527b5 100755 --- a/moderation_tool.py +++ b/moderation_tool.py @@ -56,7 +56,7 @@ while pass_token == False: print("\n#### Server Commands ####\t\t\t\t\t#### Report Generation ####") print("40) Delete and block a specific media.\t\t\t\t70) Generate user report.") print("41) Purge remote media repository up to a certain date.\t\t71) Decrypt user report .zip file.") - print("42) Prepare database for copying events of multiple rooms.") + print("42) Prepare database for copying events of multiple rooms.\t72) Send a test email.") print("43) Lookup homeserver admin contact email.") print("\n#### rdlist ####") print("50) Block all rooms with specific rdlist tags.") @@ -154,6 +154,8 @@ while pass_token == False: report_commands.generate_user_report('') elif menu_input == "71": report_commands.decrypt_zip_file() + elif menu_input == "72": + report_commands.test_send_email() elif menu_input == "q" or menu_input == "Q" or menu_input == "e" or menu_input == "E": print("\nExiting...\n") pass_token = True diff --git a/report_commands.py b/report_commands.py index 676668c..02da092 100644 --- a/report_commands.py +++ b/report_commands.py @@ -6,6 +6,12 @@ import string import datetime import zipfile import pyAesCrypt +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.base import MIMEBase +from email.mime.text import MIMEText +from email.utils import COMMASPACE +from email import encoders import user_commands import room_commands import ipinfo_commands @@ -164,3 +170,49 @@ def decrypt_zip_file(): pyAesCrypt.decryptFile(encrypted_zip_file_name, encrypted_zip_file_name[:-4], strong_password, 64 * 1024) # Print the location of the decrypted ZIP file print("\nDecrypted .zip file location: " + encrypted_zip_file_name[:-4] + "\n") + +def send_email(email_address, email_subject, email_content, email_attachments): + assert isinstance(email_attachments, list) + + msg = MIMEMultipart() # Create a multipart message + msg['From'] = hardcoded_variables.smtp_user + msg['To'] = COMMASPACE.join([email_address]) + msg['Subject'] = email_subject + + msg.attach(MIMEText(email_content)) # Attach the email body + + # Attach files + for file in email_attachments: + part = MIMEBase('application', "octet-stream") + with open(file, 'rb') as f: + part.set_payload(f.read()) + encoders.encode_base64(part) + part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(file)) + msg.attach(part) + + try: + # Send the email via SMTP server + smtp = smtplib.SMTP(hardcoded_variables.smtp_server, hardcoded_variables.smtp_port) + smtp.starttls() + smtp.login(hardcoded_variables.smtp_user, hardcoded_variables.smtp_password) + smtp.sendmail(hardcoded_variables.smtp_user, email_address, msg.as_string()) + smtp.close() + return True + except Exception as e: + print(f"Failed to send email: {e}") + return False + +def test_send_email(): + # Ask the user for the destination email address + email_address = input("\nPlease enter the destination email address to send this test email too: ") + + # Example email parameters + email_subject = "Test Email" + email_content = "This is a test email." + email_attachments = ["./test_data/evil_clown.jpeg"] # List of file paths. Adjust this to the actual files you want to attach. + + # Try to send the email + if send_email(email_address, email_subject, email_content, email_attachments): + print("\nEmail successfully sent.") + else: + print("\nFailed to send email.") diff --git a/server_commands.py b/server_commands.py index d212bcc..eecc39d 100644 --- a/server_commands.py +++ b/server_commands.py @@ -139,18 +139,23 @@ def prepare_database_copy_of_multiple_rooms(): print("\nThe sql query files have been generated, as postgres user in container run:\n# docker exec -it matrix-postgres /bin/bash\nbash-5.0$ export PGPASSWORD=your-db-password\nbash-5.0$ for f in /var/lib/postgresql/data/ramdisk/*/dump_room_data.sql; do psql --host=127.0.0.1 --port=5432 --username=synapse -w -f $f; done\n\nAfter copying the data to a cloud location law enforcement can access, clean up the ramdisk like so:\n# rm -r /matrix/postgres/data/ramdisk/*\n# umount /matrix/postgres/data/ramdisk") -def lookup_homeserver_admin_email(preset_homeserver): - if preset_homeserver == '': - homeserver = input("\nEnter the base URL to collect the admin contact details (Example: matrix.org): ") - elif preset_homeserver != '': - homeserver = preset_homeserver +def lookup_homeserver_admin_email(preset_baseurl): + if preset_baseurl == '': + baseurl = input("\nEnter the base URL to collect the admin contact details (Example: matrix.org): ") + elif preset_baseurl != '': + baseurl = preset_baseurl + + # If baseurl is matrix.org, return 'abuse@matrix.org' as a hardcoded response + if baseurl == "matrix.org": + print("\nAdmin contact email(s) for " + baseurl + " are: abuse@matrix.org") + return {"matrix.org": ["abuse@matrix.org"]}, False # Check target homserver for MSC1929 support email - url = f"https://{homeserver}/.well-known/matrix/support" + url = f"https://{baseurl}/.well-known/matrix/support" try: response = requests.get(url) except requests.exceptions.RequestException as e: - print(f"Error: Unable to connect to server {homeserver}. Trying WHOIS data...") + print(f"Error: Unable to connect to server {baseurl}. Trying WHOIS data...") response = None # If the request was successful, the status code will be 200 @@ -161,29 +166,25 @@ def lookup_homeserver_admin_email(preset_homeserver): # Extract the emails from the admins field and remove duplicates admin_emails = list({admin['email_address'] for admin in data['admins']}) - print("Admin contact emails for " + homeserver + " are: " + str(admin_emails)) + print("\nAdmin contact emails for " + baseurl + " are: " + str(admin_emails)) - # Create a dictionary with homeserver as key and emails as value - email_dict = {homeserver: admin_emails} - - # Convert the dictionary to a JSON string and print it - email_json = json.dumps(email_dict, indent=4) - print("Admin contact emails for " + homeserver + " in JSON format: " + email_json) + # Create a dictionary with baseurl as key and emails as value + email_dict = {baseurl: admin_emails} return email_dict, False else: - print(f"Error: Unable to collect admin email from server {homeserver}") + print(f"Error: Unable to collect admin email from server {baseurl}") print("Attempting to collect admin email from WHOIS data...") # Get WHOIS data try: - w = whois.whois(homeserver) + w = whois.whois(baseurl) if w.emails: - print("Admin contact email(s) for " + homeserver + " are: " + str(w.emails)) - return {homeserver: list(w.emails)}, True + print("\nAdmin contact email(s) for " + baseurl + " are: " + str(w.emails)) + return {baseurl: list(w.emails)}, True else: - print(f"Error: Unable to collect admin email from WHOIS data for {homeserver}") + print(f"Error: Unable to collect admin email from WHOIS data for {baseurl}") return None, False except: - print(f"Error: Unable to collect WHOIS data for {homeserver}") + print(f"Error: Unable to collect WHOIS data for {baseurl}") return None, False diff --git a/test_data/evil_clown.jpeg b/test_data/evil_clown.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1735d1b85cecad4098d5fbbd288720d3b5f50c4e GIT binary patch literal 10212 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 Dn7=_G literal 0 HcmV?d00001