docs: Update relnotes to remove "node_modules".
[mediagoblin.git] / mediagoblin / tools / mail.py
index 826acdbff780fdd3c9d2e80d9d3762e0a3d2818f..3dc180d86c28e7ef3a9a51953d3fcd79dd3c4231 100644 (file)
@@ -1,5 +1,5 @@
 # GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011 MediaGoblin contributors.  See AUTHORS.
+# Copyright (C) 2011, 2012 MediaGoblin contributors.  See AUTHORS.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+from __future__ import print_function, unicode_literals
+
+import socket
+import logging
+import six
 import smtplib
-from email.MIMEText import MIMEText
-from mediagoblin import mg_globals
+import sys
+from mediagoblin import mg_globals, messages
+from mediagoblin._compat import MIMEText
 from mediagoblin.tools import common
 
 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -24,7 +30,7 @@ from mediagoblin.tools import common
 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 # We have two "test inboxes" here:
-# 
+#
 # EMAIL_TEST_INBOX:
 # ----------------
 #   If you're writing test views, you'll probably want to check this.
@@ -44,11 +50,20 @@ from mediagoblin.tools import common
 # ***IMPORTANT!***
 # ----------------
 # Before running tests that call functions which send email, you should
-# always call _clear_test_inboxes() to "wipe" the inboxes clean. 
+# always call _clear_test_inboxes() to "wipe" the inboxes clean.
 
 EMAIL_TEST_INBOX = []
 EMAIL_TEST_MBOX_INBOX = []
 
+
+class MailError(Exception):
+    """ General exception for mail errors """
+
+
+class NoSMTPServerError(MailError):
+    pass
+
+
 class FakeMhost(object):
     """
     Just a fake mail host so we can capture and test messages
@@ -63,12 +78,16 @@ class FakeMhost(object):
              'to': to_addrs,
              'message': message})
 
+    def starttls(self):
+        raise smtplib.SMTPException("No STARTTLS here")
+
 def _clear_test_inboxes():
     global EMAIL_TEST_INBOX
     global EMAIL_TEST_MBOX_INBOX
     EMAIL_TEST_INBOX = []
     EMAIL_TEST_MBOX_INBOX = []
 
+
 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ### </Special email test stuff>
 ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -87,16 +106,43 @@ def send_email(from_addr, to_addrs, subject, message_body):
     if common.TESTS_ENABLED or mg_globals.app_config['email_debug_mode']:
         mhost = FakeMhost()
     elif not mg_globals.app_config['email_debug_mode']:
-        mhost = smtplib.SMTP(
-            mg_globals.app_config['email_smtp_host'],
-            mg_globals.app_config['email_smtp_port'])
+        if mg_globals.app_config['email_smtp_use_ssl']:
+            smtp_init = smtplib.SMTP_SSL
+        else:
+            smtp_init = smtplib.SMTP
+
+        try:
+            mhost = smtp_init(
+                mg_globals.app_config['email_smtp_host'],
+                mg_globals.app_config['email_smtp_port'])
+        except socket.error as original_error:
+            error_message = "Couldn't contact mail server on <{}>:<{}>".format(
+                mg_globals.app_config['email_smtp_host'],
+                mg_globals.app_config['email_smtp_port'])
+            logging.debug(original_error)
+            raise NoSMTPServerError(error_message)
 
         # SMTP.__init__ Issues SMTP.connect implicitly if host
         if not mg_globals.app_config['email_smtp_host']:  # e.g. host = ''
-            mhost.connect()  # We SMTP.connect explicitly
-
-    if mg_globals.app_config['email_smtp_user'] \
-            or mg_globals.app_config['email_smtp_pass']:
+            try:
+                mhost.connect()  # We SMTP.connect explicitly
+            except socket.error as original_error:
+                error_message = "Couldn't contact mail server on <{}>:<{}>".format(
+                    mg_globals.app_config['email_smtp_host'],
+                    mg_globals.app_config['email_smtp_port'])
+                logging.debug(original_error)
+                raise NoSMTPServerError(error_message)
+
+        try:
+            mhost.starttls()
+        except smtplib.SMTPException:
+            # Only raise an exception if we're forced to
+            if mg_globals.app_config['email_smtp_force_starttls']:
+                six.reraise(*sys.exc_info())
+
+    if ((not common.TESTS_ENABLED)
+        and (mg_globals.app_config['email_smtp_user']
+             or mg_globals.app_config['email_smtp_pass'])):
         mhost.login(
             mg_globals.app_config['email_smtp_user'],
             mg_globals.app_config['email_smtp_pass'])
@@ -109,12 +155,40 @@ def send_email(from_addr, to_addrs, subject, message_body):
     if common.TESTS_ENABLED:
         EMAIL_TEST_INBOX.append(message)
 
-    if mg_globals.app_config['email_debug_mode']:
-        print u"===== Email ====="
-        print u"From address: %s" % message['From']
-        print u"To addresses: %s" % message['To']
-        print u"Subject: %s" % message['Subject']
-        print u"-- Body: --"
-        print message.get_payload(decode=True)
+    elif mg_globals.app_config['email_debug_mode']:
+        print("===== Email =====")
+        print("From address: %s" % message['From'])
+        print("To addresses: %s" % message['To'])
+        print("Subject: %s" % message['Subject'])
+        print("-- Body: --")
+        print(message_body)
 
     return mhost.sendmail(from_addr, to_addrs, message.as_string())
+
+
+def normalize_email(email):
+    """return case sensitive part, lower case domain name
+
+    :returns: None in case of broken email addresses"""
+    try:
+        em_user, em_dom = email.split('@', 1)
+    except ValueError:
+        # email contained no '@'
+        return None
+    email = "@".join((em_user, em_dom.lower()))
+    return email
+
+
+def email_debug_message(request):
+    """
+    If the server is running in email debug mode (which is
+    the current default), give a debug message to the user
+    so that they have an idea where to find their email.
+    """
+    if mg_globals.app_config['email_debug_mode']:
+        # DEBUG message, no need to translate
+        messages.add_message(
+            request,
+            messages.DEBUG,
+            "This instance is running in email debug mode. "
+            "The email will be on the console of the server process.")