Use currently-logged-in user to authenticate for mass email sending.
authorPatrick Melanson <pj2melan@csclub.uwaterloo.ca>
Mon, 28 Aug 2017 20:51:14 +0000 (16:51 -0400)
committerPatrick Melanson <pj2melan@csclub.uwaterloo.ca>
Sat, 23 Sep 2017 05:24:23 +0000 (01:24 -0400)
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
library/interface/form.py
library/interface/sendemails.py

index b5d34fa..d17d296 100644 (file)
@@ -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()
 
index 1002ba2..df1b455 100644 (file)
@@ -283,19 +283,6 @@ class FormWindow:
             if self.bt==-1:
                 self.entries[self.hl].handle_input(ch)
 
-class LoginForm(FormWindow):
-  caption = "Enter your csclub login to access the mail server"
-  blabel = "Login"
-  labels = ["ID", "Password"]
-
-  def _make_entries(self):
-        self.entries = []
-        self.entries.append(TextEntry(self.w))
-        self.entries.append(PasswordEntry(self.w))
-
-  def _return_values(self):
-    return [self.entries[0].value, self.entries[1].value]
-
 class BookForm(FormWindow):
     caption = "Add a Book"
     blabel = "Add"
index 8304469..d2bc050 100644 (file)
@@ -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 <email_message> | 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 = """