mail handler
Tony Mack [Mon, 23 Jun 2008 17:12:37 +0000 (17:12 +0000)]
qaapi/qa/sendmail.py [new file with mode: 0644]

diff --git a/qaapi/qa/sendmail.py b/qaapi/qa/sendmail.py
new file mode 100644 (file)
index 0000000..727ae77
--- /dev/null
@@ -0,0 +1,91 @@
+import os
+import sys
+import pprint
+from types import StringTypes
+from email.MIMEText import MIMEText
+from email.Header import Header
+from smtplib import SMTP
+
+
+def sendmail(To, Subject, Body, From = None, Cc = None, Bcc = None):
+    """
+    Uses sendmail (must be installed and running locally) to send a
+    message to the specified recipients. If the API is running under
+    mod_python, the apache user must be listed in e.g.,
+    /etc/mail/trusted-users.
+
+    To, Cc, and Bcc may be addresses or lists of addresses. Each
+    address may be either a plain text address or a tuple of (name,
+    address).
+    """
+
+    # Fix up defaults
+    if not isinstance(To, list):
+        To = [To]
+    if Cc is not None and not isinstance(Cc, list):
+        Cc = [Cc]
+    if Bcc is not None and not isinstance(Bcc, list):
+        Bcc = [Bcc]
+    if From is None:
+        From = ("Quality Assurance <qa@planet-lab.org>")
+
+    # Create a MIME-encoded UTF-8 message
+    msg = MIMEText(Body.encode("utf-8"), _charset = "utf-8")
+
+    # Unicode subject headers are automatically encoded correctly
+    msg['Subject'] = Subject
+
+    def encode_addresses(addresses, header_name = None):
+        """
+        Unicode address headers are automatically encoded by
+        email.Header, but not correctly. The correct way is to put the
+        textual name inside quotes and the address inside brackets:
+
+        To: "=?utf-8?b?encoded" <recipient@domain>
+
+        Each address in addrs may be a tuple of (name, address) or
+        just an address. Returns a tuple of (header, addrlist)
+        representing the encoded header text and the list of plain
+        text addresses.
+        """
+
+        header = []
+        addrs = []
+
+        for addr in addresses:
+            if isinstance(addr, tuple):
+                (name, addr) = addr
+                try:
+                    name = name.encode('ascii')
+                    header.append('%s <%s>' % (name, addr))
+                except:
+                    h = Header(name, charset = "utf-8", header_name = header_name)
+                    header.append('"%s" <%s>' % (h.encode(), addr))
+            else:
+                header.append(addr)
+            addrs.append(addr)
+
+        return (", ".join(header), addrs)
+
+    (msg['From'], from_addrs) = encode_addresses([From], 'From')
+    (msg['To'], to_addrs) = encode_addresses(To, 'To')
+
+    if Cc is not None:
+        (msg['Cc'], cc_addrs) = encode_addresses(Cc, 'Cc')
+        to_addrs += cc_addrs
+
+    if Bcc is not None:
+        (unused, bcc_addrs) = encode_addresses(Bcc, 'Bcc')
+        to_addrs += bcc_addrs
+
+    # Needed to pass some spam filters
+    msg['Reply-To'] = msg['From']
+    msg['X-Mailer'] = "Python/" + sys.version.split(" ")[0]
+
+    s = SMTP()
+    s.connect()
+    rejected = s.sendmail(from_addrs[0], to_addrs, msg.as_string(), rcpt_options = ["NOTIFY=NEVER"])
+    s.close()
+
+    if rejected:
+        raise Exception, "Error sending message to " + ", ".join(rejected.keys())