diff roundup/mailer.py @ 1799:071ea6fc803f

Extracted duplicated mail-sending code... ...from mailgw, roundupdb and client.py to the new mailer.py module.
author Johannes Gijsbers <jlgijsbers@users.sourceforge.net>
date Mon, 08 Sep 2003 09:28:28 +0000
parents
children ee33ce4987f5
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/roundup/mailer.py	Mon Sep 08 09:28:28 2003 +0000
@@ -0,0 +1,148 @@
+"""Sending Roundup-specific mail over SMTP."""
+# $Id: mailer.py,v 1.1 2003-09-08 09:28:28 jlgijsbers Exp $
+
+import time, quopri, os, socket, smtplib, re
+
+from cStringIO import StringIO
+from MimeWriter import MimeWriter
+
+from roundup.rfc2822 import encode_header
+
+class MessageSendError(RuntimeError):
+    pass
+
+class Mailer:
+    """Roundup-specific mail sending."""
+    def __init__(self, config):
+        self.config = config
+
+        # set to indicate to roundup not to actually _send_ email
+        # this var must contain a file to write the mail to
+        self.debug = os.environ.get('SENDMAILDEBUG', '')
+
+    def get_standard_message(self, to, subject, author=None):
+        if not author:
+            author = straddr((self.config.TRACKER_NAME,
+                              self.config.ADMIN_EMAIL))
+        message = StringIO()
+        writer = MimeWriter(message)
+        writer.addheader('Subject', encode_header(subject))
+        writer.addheader('To', to)
+        writer.addheader('From', author)
+        writer.addheader('Date', time.strftime("%a, %d %b %Y %H:%M:%S +0000",
+                                               time.gmtime()))
+
+        # Add a unique Roundup header to help filtering
+        writer.addheader('X-Roundup-Name', self.config.TRACKER_NAME)
+        # and another one to avoid loops
+        writer.addheader('X-Roundup-Loop', 'hello')
+
+        writer.addheader('MIME-Version', '1.0')       
+        
+        return message, writer
+
+    def standard_message(self, to, subject, content):
+        message, writer = self.get_standard_message(to, subject)
+
+        writer.addheader('Content-Transfer-Encoding', 'quoted-printable')
+        body = writer.startbody('text/plain; charset=utf-8')
+        content = StringIO(content)
+        quopri.encode(content, body, 0)
+
+        self.smtp_send(to, message)
+       
+    def bounce_message(self, bounced_message, to, error,
+                       subject='Failed issue tracker submission'):
+        message, writer = self.get_standard_message(', '.join(to), subject)
+
+        part = writer.startmultipartbody('mixed')
+        part = writer.nextpart()
+        part.addheader('Content-Transfer-Encoding', 'quoted-printable')
+        body = part.startbody('text/plain; charset=utf-8')
+        body.write('\n'.join(error))
+
+        # attach the original message to the returned message
+        part = writer.nextpart()
+        part.addheader('Content-Disposition', 'attachment')
+        part.addheader('Content-Description', 'Message you sent')
+        body = part.startbody('text/plain')
+
+        for header in bounced_message.headers:
+            body.write(header)
+        body.write('\n')
+        try:
+            bounced_message.rewindbody()
+        except IOError, message:
+            body.write("*** couldn't include message body: %s ***"
+                       % bounced_message)
+        else:
+            body.write(bounced_message.fp.read())
+
+        writer.lastpart()
+
+        self.smtp_send(to, message)
+        
+    def smtp_send(self, to, message):
+        if self.debug:
+            # don't send - just write to a file
+            open(self.debug, 'a').write('FROM: %s\nTO: %s\n%s\n' %
+                                        (self.config.ADMIN_EMAIL,
+                                         ', '.join(to),
+                                         message.getvalue()))
+        else:
+            # now try to send the message
+            try:
+                # send the message as admin so bounces are sent there
+                # instead of to roundup
+                smtp = SMTPConnection(self.config)
+                smtp.sendmail(self.config.ADMIN_EMAIL, [to],
+                              message.getvalue())
+            except socket.error, value:
+                raise MessageSendError("Error: couldn't send email: "
+                                       "mailhost %s"%value)
+            except smtplib.SMTPException, msg:
+                raise MessageSendError("Error: couldn't send email: %s"%msg)
+
+class SMTPConnection(smtplib.SMTP):
+    ''' Open an SMTP connection to the mailhost specified in the config
+    '''
+    def __init__(self, config):
+        
+        smtplib.SMTP.__init__(self, config.MAILHOST)
+
+        # use TLS?
+        use_tls = getattr(config, 'MAILHOST_TLS', 'no')
+        if use_tls == 'yes':
+            # do we have key files too?
+            keyfile = getattr(config, 'MAILHOST_TLS_KEYFILE', '')
+            if keyfile:
+                certfile = getattr(config, 'MAILHOST_TLS_CERTFILE', '')
+                if certfile:
+                    args = (keyfile, certfile)
+                else:
+                    args = (keyfile, )
+            else:
+                args = ()
+            # start the TLS
+            self.starttls(*args)
+
+        # ok, now do we also need to log in?
+        mailuser = getattr(config, 'MAILUSER', None)
+        if mailuser:
+            self.login(*config.MAILUSER)
+
+# use the 'email' module, either imported, or our copied version
+try :
+    from email.Utils import formataddr as straddr
+except ImportError :
+    # code taken from the email package 2.4.3
+    def straddr(pair, specialsre = re.compile(r'[][\()<>@,:;".]'),
+            escapesre = re.compile(r'[][\()"]')):
+        name, address = pair
+        if name:
+            quotes = ''
+            if specialsre.search(name):
+                quotes = '"'
+            name = escapesre.sub(r'\\\g<0>', name)
+            return '%s%s%s <%s>' % (quotes, name, quotes, address)
+        return address

Roundup Issue Tracker: http://roundup-tracker.org/