summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 0d693ff)
raw | patch | inline | side by side (parent: 0d693ff)
author | jlgijsbers <jlgijsbers@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Mon, 8 Sep 2003 09:28:28 +0000 (09:28 +0000) | ||
committer | jlgijsbers <jlgijsbers@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Mon, 8 Sep 2003 09:28:28 +0000 (09:28 +0000) |
the new mailer.py module.
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1866 57a73879-2fb5-44c3-a270-3262357dd7e2
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1866 57a73879-2fb5-44c3-a270-3262357dd7e2
roundup/cgi/client.py | patch | blob | history | |
roundup/mailer.py | [new file with mode: 0644] | patch | blob |
roundup/mailgw.py | patch | blob | history | |
roundup/roundupdb.py | patch | blob | history |
diff --git a/roundup/cgi/client.py b/roundup/cgi/client.py
index d4228e1780de94b9a1d87fc7f9c362d625d9c1a8..ea1ad7359af8048f831407d3afced4486f1c06e5 100644 (file)
--- a/roundup/cgi/client.py
+++ b/roundup/cgi/client.py
-# $Id: client.py,v 1.135 2003-09-07 22:12:24 richard Exp $
+# $Id: client.py,v 1.136 2003-09-08 09:28:28 jlgijsbers Exp $
__doc__ = """
WWW request handler (also used in the stand-alone server).
from roundup.cgi import cgitb
from roundup.cgi.PageTemplates import PageTemplate
from roundup.rfc2822 import encode_header
-from roundup.mailgw import uidFromAddress, openSMTPConnection
+from roundup.mailgw import uidFromAddress
+from roundup.mailer import Mailer, MessageSendError
class HTTPException(Exception):
pass
class NotModified(HTTPException):
pass
-# set to indicate to roundup not to actually _send_ email
-# this var must contain a file to write the mail to
-SENDMAILDEBUG = os.environ.get('SENDMAILDEBUG', '')
-
# used by a couple of routines
if hasattr(string, 'ascii_letters'):
chars = string.ascii_letters+string.digits
self.instance = instance
self.request = request
self.env = env
+ self.mailer = Mailer(instance.config)
# save off the path
self.path = env['PATH_INFO']
%(url)s?@action=confrego&otk=%(otk)s
'''%{'name': props['username'], 'tracker': tracker_name, 'url': self.base,
'otk': otk}
- if not self.sendEmail(props['address'], subject, body):
+ if not self.standard_message(props['address'], subject, body):
return
# commit changes to the database
# redirect to the "you're almost there" page
raise Redirect, '%suser?@template=rego_progress'%self.base
- def sendEmail(self, to, subject, content):
- # send email to the user's email address
- message = StringIO.StringIO()
- writer = MimeWriter.MimeWriter(message)
- tracker_name = self.db.config.TRACKER_NAME
- writer.addheader('Subject', encode_header(subject))
- writer.addheader('To', to)
- writer.addheader('From', roundupdb.straddr((tracker_name,
- self.db.config.ADMIN_EMAIL)))
- writer.addheader('Date', time.strftime("%a, %d %b %Y %H:%M:%S +0000",
- time.gmtime()))
- # add a uniquely Roundup header to help filtering
- writer.addheader('X-Roundup-Name', tracker_name)
- # avoid email loops
- writer.addheader('X-Roundup-Loop', 'hello')
- writer.addheader('Content-Transfer-Encoding', 'quoted-printable')
- body = writer.startbody('text/plain; charset=utf-8')
-
- # message body, encoded quoted-printable
- content = StringIO.StringIO(content)
- quopri.encode(content, body, 0)
-
- if SENDMAILDEBUG:
- # don't send - just write to a file
- open(SENDMAILDEBUG, 'a').write('FROM: %s\nTO: %s\n%s\n'%(
- self.db.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 = openSMTPConnection(self.db.config)
- smtp.sendmail(self.db.config.ADMIN_EMAIL, [to],
- message.getvalue())
- except socket.error, value:
- self.error_message.append("Error: couldn't send email: "
- "mailhost %s"%value)
- return 0
- except smtplib.SMTPException, msg:
- self.error_message.append("Error: couldn't send email: %s"%msg)
- return 0
- return 1
+ def standard_message(self, to, subject, body):
+ try:
+ self.mailer.standard_message(to, subject, body)
+ return 1
+ except MessageSendException, e:
+ self.error_message.append(str(e))
+
def registerPermission(self, props):
''' Determine whether the user has permission to register
Your password is now: %(password)s
'''%{'name': name, 'password': newpw}
- if not self.sendEmail(address, subject, body):
+ if not self.standard_message(address, subject, body):
return
self.ok_message.append('Password reset and email sent to %s'%address)
You should then receive another email with the new password.
'''%{'name': name, 'tracker': tracker_name, 'url': self.base, 'otk': otk}
- if not self.sendEmail(address, subject, body):
+ if not self.standard_message(address, subject, body):
return
self.ok_message.append('Email sent to %s'%address)
diff --git a/roundup/mailer.py b/roundup/mailer.py
--- /dev/null
+++ b/roundup/mailer.py
@@ -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
diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index a07fafde2750bfe61fe9e269e16cb25d9a135400..f6822aa0a33e4eb75e5cbfa37a6d1e04f3f39a0c 100644 (file)
--- a/roundup/mailgw.py
+++ b/roundup/mailgw.py
an exception, the original message is bounced back to the sender with the
explanatory message given in the exception.
-$Id: mailgw.py,v 1.129 2003-09-06 10:37:11 jlgijsbers Exp $
+$Id: mailgw.py,v 1.130 2003-09-08 09:28:28 jlgijsbers Exp $
"""
import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri
import traceback, MimeWriter, rfc822
from roundup import hyperdb, date, password, rfc2822
+from roundup.mailer import Mailer
SENDMAILDEBUG = os.environ.get('SENDMAILDEBUG', '')
return rfc822.unquote(f[i+1:].strip())
return None
-def openSMTPConnection(config):
- ''' Open an SMTP connection to the mailhost specified in the config
- '''
- smtp = smtplib.SMTP(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
- smtp.starttls(*args)
-
- # ok, now do we also need to log in?
- mailuser = getattr(config, 'MAILUSER', None)
- if mailuser:
- smtp.login(*config.MAILUSER)
-
- # that's it, a fully-configured SMTP connection ready to go
- return smtp
-
class Message(mimetools.Message):
''' subclass mimetools.Message so we can retrieve the parts of the
message...
self.instance = instance
self.db = db
self.arguments = arguments
+ self.mailer = Mailer(instance.config)
# should we trap exceptions (normal usage) or pass them through
# (for testing)
m = ['']
m.append('\n\nMail Gateway Help\n=================')
m.append(fulldoc)
- m = self.bounce_message(message, sendto, m,
+ self.mailer.bounce_message(message, sendto, m,
subject="Mail Gateway Help")
except MailUsageError, value:
# bounce the message back to the sender with the usage message
m.append(str(value))
m.append('\n\nMail Gateway Help\n=================')
m.append(fulldoc)
- m = self.bounce_message(message, sendto, m)
+ self.mailer.bounce_message(message, sendto, m)
except Unauthorized, value:
# just inform the user that he is not authorized
sendto = [sendto[0][1]]
m = ['']
m.append(str(value))
- m = self.bounce_message(message, sendto, m)
+ self.mailer.bounce_message(message, sendto, m)
except MailLoop:
# XXX we should use a log file here...
return
import traceback
traceback.print_exc(None, s)
m.append(s.getvalue())
- m = self.bounce_message(message, sendto, m)
+ self.mailer.bounce_message(message, sendto, m)
else:
# very bad-looking message - we don't even know who sent it
# XXX we should use a log file here...
m.append('line, indicating that it is corrupt. Please check your')
m.append('mail gateway source. Failed message is attached.')
m.append('')
- m = self.bounce_message(message, sendto, m,
+ self.mailer.bounce_message(message, sendto, m,
subject='Badly formed message from mail gateway')
- # now send the message
- if SENDMAILDEBUG:
- open(SENDMAILDEBUG, 'a').write('From: %s\nTo: %s\n%s\n'%(
- self.instance.config.ADMIN_EMAIL, ', '.join(sendto),
- m.getvalue()))
- else:
- try:
- smtp = openSMTPConnection(self.instance.config)
- smtp.sendmail(self.instance.config.ADMIN_EMAIL, sendto,
- m.getvalue())
- except socket.error, value:
- raise MailGWError, "Couldn't send error email: "\
- "mailhost %s"%value
- except smtplib.SMTPException, value:
- raise MailGWError, "Couldn't send error email: %s"%value
-
- def bounce_message(self, message, sendto, error,
- subject='Failed issue tracker submission'):
- ''' create a message that explains the reason for the failed
- issue submission to the author and attach the original
- message.
- '''
- msg = cStringIO.StringIO()
- writer = MimeWriter.MimeWriter(msg)
- writer.addheader('X-Roundup-Loop', 'hello')
- writer.addheader('Subject', subject)
- writer.addheader('From', '%s <%s>'% (self.instance.config.TRACKER_NAME,
- self.instance.config.TRACKER_EMAIL))
- writer.addheader('To', ','.join(sendto))
- writer.addheader('Date', time.strftime("%a, %d %b %Y %H:%M:%S +0000",
- time.gmtime()))
- writer.addheader('MIME-Version', '1.0')
- part = writer.startmultipartbody('mixed')
- part = writer.nextpart()
- 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 message.headers:
- body.write(header)
- body.write('\n')
- try:
- message.rewindbody()
- except IOError, message:
- body.write("*** couldn't include message body: %s ***"%message)
- else:
- body.write(message.fp.read())
-
- writer.lastpart()
- return msg
-
def get_part_data_decoded(self,part):
encoding = part.getencoding()
data = None
diff --git a/roundup/roundupdb.py b/roundup/roundupdb.py
index 14e0622d491b9a80918d3fe9c069422220198615..5bc4928a7af2e94a67591345661b6602e37d9a35 100644 (file)
--- a/roundup/roundupdb.py
+++ b/roundup/roundupdb.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: roundupdb.py,v 1.88 2003-09-06 20:02:23 jlgijsbers Exp $
+# $Id: roundupdb.py,v 1.89 2003-09-08 09:28:28 jlgijsbers Exp $
__doc__ = """
Extending hyperdb with types specific to issue-tracking.
"""
import re, os, smtplib, socket, time, random
-import MimeWriter, cStringIO
-import base64, quopri, mimetypes
+import cStringIO, base64, quopri, mimetypes
from rfc2822 import encode_header
-from roundup import password, date
-
-# if available, use the 'email' module, otherwise fallback to 'rfc822'
-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
-
-from roundup import hyperdb
-from roundup.mailgw import openSMTPConnection
-
-# set to indicate to roundup not to actually _send_ email
-# this var must contain a file to write the mail to
-SENDMAILDEBUG = os.environ.get('SENDMAILDEBUG', '')
+from roundup import password, date, hyperdb
+
+# MessageSendError is imported for backwards compatibility
+from roundup.mailer import Mailer, straddr, MessageSendError
class Database:
def getuid(self):
return userid
-class MessageSendError(RuntimeError):
- pass
class DetectorError(RuntimeError):
- ''' Raised by detectors that want to indicate that something's amiss
- '''
+ """ Raised by detectors that want to indicate that something's amiss
+ """
pass
# deviation from spec - was called IssueClass
if from_tag:
from_tag = ' ' + from_tag
+ subject = '[%s%s] %s' % (cn, nodeid, encode_header(title))
+ author = straddr((encode_header(authname) + from_tag, from_address))
+
# create the message
- message = cStringIO.StringIO()
- writer = MimeWriter.MimeWriter(message)
- writer.addheader('Subject', '[%s%s] %s'%(cn, nodeid,
- encode_header(title)))
- writer.addheader('To', ', '.join(sendto))
- writer.addheader('From', straddr((encode_header(authname) +
- from_tag, from_address)))
+ mailer = Mailer(self.db.config)
+ message, writer = mailer.get_standard_message(', '.join(sendto),
+ subject, author)
+
tracker_name = encode_header(self.db.config.TRACKER_NAME)
writer.addheader('Reply-To', straddr((tracker_name, from_address)))
- writer.addheader('Date', time.strftime("%a, %d %b %Y %H:%M:%S +0000",
- time.gmtime()))
- writer.addheader('MIME-Version', '1.0')
if messageid:
writer.addheader('Message-Id', messageid)
if inreplyto:
writer.addheader('In-Reply-To', inreplyto)
- # add a uniquely Roundup header to help filtering
- writer.addheader('X-Roundup-Name', tracker_name)
-
- # avoid email loops
- writer.addheader('X-Roundup-Loop', 'hello')
-
# attach files
if message_files:
part = writer.startmultipartbody('mixed')
body = writer.startbody('text/plain; charset=utf-8')
body.write(content_encoded)
- # now try to send the message
- if SENDMAILDEBUG:
- open(SENDMAILDEBUG, 'a').write('FROM: %s\nTO: %s\n%s\n'%(
- self.db.config.ADMIN_EMAIL,
- ', '.join(sendto),message.getvalue()))
- else:
- try:
- # send the message as admin so bounces are sent there
- # instead of to roundup
- smtp = openSMTPConnection(self.db.config)
- smtp.sendmail(self.db.config.ADMIN_EMAIL, sendto,
- message.getvalue())
- except socket.error, value:
- raise MessageSendError, \
- "Couldn't send confirmation email: mailhost %s"%value
- except smtplib.SMTPException, value:
- raise MessageSendError, \
- "Couldn't send confirmation email: %s"%value
+ mailer.smtp_send(sendto, message)
def email_signature(self, nodeid, msgid):
''' Add a signature to the e-mail with some useful information