From 73bbf6bb33d99bd70245ae16d2acadde97fa1806 Mon Sep 17 00:00:00 2001 From: Patrick Melanson Date: Mon, 28 Aug 2017 16:51:14 -0400 Subject: [PATCH] Use currently-logged-in user to authenticate for mass email sending. Basically, this commit removes email sending code that asked the user for a CSC username and email to authenticate to an SMTP server to send a mass email. Now, we just use the system utility 'sendmail' to do it, which means you don't have to enter in your email creds to send a mass email once you're already logged in. Magic! --- library/emails.py | 21 +++++------- library/interface/sendemails.py | 60 +++++++++------------------------ 2 files changed, 25 insertions(+), 56 deletions(-) diff --git a/library/emails.py b/library/emails.py index b5d34fa..d17d296 100644 --- a/library/emails.py +++ b/library/emails.py @@ -1,3 +1,5 @@ +from email.mime.text import MIMEText + def format_reminder_email(quest_id: str, days_signed_out: int, librarian_name: str, @@ -6,7 +8,7 @@ def format_reminder_email(quest_id: str, Formats an email as a plain string for sending out email reminders for signed out books. - Example: _send_email("s455wang", "2017-02-04", 30, "csfmurph", "How to Design Programs") + Example: format_reminder_email("s455wang", 30, "Connor Murphy", "How to Design Programs") """ assert len(quest_id) <= 8 assert quest_id.isalnum() @@ -14,7 +16,7 @@ def format_reminder_email(quest_id: str, assert librarian_name != "" assert book_name != "" - email_message_body = \ + email_message = MIMEText( """Hi {}, Our records indicate that you have had the book {} signed out for {} days. @@ -32,16 +34,11 @@ librarian@csclub.uwaterloo.ca""".format( book_name, days_signed_out, librarian_name - ) + )) - email_message_subject = "Overdue book: {}".format(book_name) - email_message = ( - "To: \"{0}@csclub.uwaterloo.ca\" <{0}@csclub.uwaterloo.ca>\n".format(quest_id) + - "Subject: {}\n".format(email_message_subject) + - "\n" + - email_message_body - ) - assert email_message.replace("\n", "").isprintable(), \ + email_message["Subject"] = "Overdue book: {}".format(book_name) + email_message["To"] = "\"{0}@csclub.uwaterloo.ca\" <{0}@csclub.uwaterloo.ca>\n".format(quest_id) + assert email_message.as_string().replace("\n", "").isprintable(), \ "Our email should not have characters apart from normal characters and newline" - return email_message + return email_message.as_string() diff --git a/library/interface/sendemails.py b/library/interface/sendemails.py index 8304469..d2bc050 100644 --- a/library/interface/sendemails.py +++ b/library/interface/sendemails.py @@ -14,14 +14,15 @@ import time #Import getpass for password input import getpass -#Import subprocess for validation of if a user is in a group +#Import subprocess for validation of if a user is in a group, and running +#a sendmail process import subprocess #Import librarian permissions from library import permissions from library.exceptions import * import library.database as db -from library.interface.form import FormWindow, BookForm, LoginForm, SuccessForm, catch_error_with, error_form +from library.interface.form import FormWindow, BookForm, SuccessForm, catch_error_with, error_form from library.emails import format_reminder_email # email testing folder creation @@ -91,11 +92,15 @@ def _send_email(quest_id: str, os.makedirs("test_emails", exist_ok=True) #Know that book titles such as "FORTRAN/77" exist, sanitize slashes. filename = ("test_emails/{}_{}.txt" - .format(quest_id, book_name.replace('/', '-'))) - with open(filename, 'w') as f: + .format(quest_id, book_name.replace("/", "-"))) + with open(filename, "w") as f: print(email_message, file=f) else: - server.sendmail("librarian@csclub.uwaterloo.ca", email_addresses, email_message) + #Equivalent to: + #echo | sendmail questid@csclub.uwaterloo.ca + sendmail_process = subprocess.Popen(["sendmail"] + email_addresses, + stdin=subprocess.PIPE) + sendmail_process.communicate(input=email_message.encode("utf-8")) return True return False @@ -129,40 +134,10 @@ def sendemails_procedure(w, hb, cy, cx, mx): if librarianName == {}: return "" #User cancelled the dialog box - #Set up email - global server - server = smtplib.SMTP_SSL("mail.csclub.uwaterloo.ca", 465) - - # don't send out emails if this is true + #Don't send out emails if this gets set to True testing = False - - #Attempt to login 3 times - loginAttempts = 0 - while loginAttempts < MAX_LOGIN_ATTEMPTS: - - #Get the user's login info to login to the mail server - step3 = LoginForm(w, hb, width=mx - 20) - (r, c) = w.getmaxyx() - w.mvwin(cy - r // 2, cx - c // 2) - loginInfo = step3.event_loop() - step3.clear() - - csclubID = loginInfo[0] - csclubPwd = loginInfo[1] - - if csclubID == "TESTING12345": - testing = True - break - - try: - server.login(csclubID, csclubPwd) - break - except smtplib.SMTPAuthenticationError: - loginAttempts += 1 - - #Check to see if login failed - if loginAttempts >= MAX_LOGIN_ATTEMPTS: - return "" + if librarianName == "TESTING12345": + testing = True #Get the books that are signed out signed_out_books = db.get_checkedout_books() @@ -182,20 +157,17 @@ def sendemails_procedure(w, hb, cy, cx, mx): earliest_checkout = min(earliest_checkout, datetime.strptime(date, "%Y-%m-%d")) - #Exit from the email server - server.quit() - #A success message about what emails we just sent assert earliest_checkout > datetime(2010,1,1) - time_since_earliest_checkout = '' + time_since_earliest_checkout = "" days_since_earliest_checkout = (datetime.now().toordinal() - earliest_checkout.toordinal()) assert days >= 0 month_length = 30 #It's wrong because it's an approximation if days_since_earliest_checkout < month_length: - time_since_earliest_checkout = '{} days ago'.format(days_since_earliest_checkout) + time_since_earliest_checkout = "{} days ago".format(days_since_earliest_checkout) else: - time_since_earliest_checkout = '{:0.1f} months ago'.format(days_since_earliest_checkout / month_length) + time_since_earliest_checkout = "{:0.1f} months ago".format(days_since_earliest_checkout / month_length) if emails_sent == 0: success_text = """