diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index 501da003ca21070f568fb94b37c76864d968ff23..9de891be5ccd136bbbc6de31f5b9800ab533c9bb 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.94 2002-10-03 06:56:29 richard Exp $
+$Id: mailgw.py,v 1.102 2002-12-11 01:52:20 richard Exp $
'''
import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri
class MailUsageHelp(Exception):
pass
+class MailLoop(Exception):
+ ''' We've seen this message before... '''
+ pass
+
class Unauthorized(Exception):
""" Access denied """
s.seek(0)
return Message(s)
-subject_re = re.compile(r'(?P<refwd>\s*\W?\s*(fwd|re|aw)\s*\W?\s*)*'
+subject_re = re.compile(r'(?P<refwd>\s*\W?\s*(fwd|re|aw)\W\s*)*'
r'\s*(?P<quote>")?(\[(?P<classname>[^\d\s]+)(?P<nodeid>\d+)?\])?'
r'\s*(?P<title>[^[]+)?"?(\[(?P<args>.+?)\])?', re.I)
def do_pipe(self):
''' Read a message from standard input and pass it to the mail handler.
+
+ Read into an internal structure that we can seek on (in case
+ there's an error).
+
+ XXX: we may want to read this into a temporary file instead...
'''
- self.main(sys.stdin)
+ s = cStringIO.StringIO()
+ s.write(sys.stdin.read())
+ s.seek(0)
+ self.main(s)
return 0
def do_mailbox(self, filename):
m = ['']
m.append(str(value))
m = self.bounce_message(message, sendto, m)
+ except MailLoop:
+ # XXX we should use a log file here...
+ return
except:
# bounce the message back to the sender with the error message
+ # XXX we should use a log file here...
sendto = [sendto[0][1], self.instance.config.ADMIN_EMAIL]
m = ['']
m.append('An unexpected error occurred during the processing')
m = self.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...
sendto = [self.instance.config.ADMIN_EMAIL]
m = ['Subject: badly formed message from mail gateway']
m.append('')
'''
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')
body.write('\n'.join(error))
- # reconstruct the original message
- m = cStringIO.StringIO()
- w = MimeWriter.MimeWriter(m)
- # default the content_type, just in case...
- content_type = 'text/plain'
- # add the headers except the content-type
+ # 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:
- header_name = header.split(':')[0]
- if header_name.lower() == 'content-type':
- content_type = message.getheader(header_name)
- elif message.getheader(header_name):
- w.addheader(header_name, message.getheader(header_name))
- # now attach the message body
- body = w.startbody(content_type)
+ body.write(header)
+ body.write('\n')
try:
message.rewindbody()
- except IOError:
- body.write("*** couldn't include message body: read from pipe ***")
+ except IOError, message:
+ body.write("*** couldn't include message body: %s ***"%message)
else:
body.write(message.fp.read())
- # attach the original message to the returned message
- part = writer.nextpart()
- part.addheader('Content-Disposition','attachment')
- part.addheader('Content-Description','Message you sent')
- part.addheader('Content-Transfer-Encoding', '7bit')
- body = part.startbody('message/rfc822')
- body.write(m.getvalue())
-
writer.lastpart()
return msg
Parse the message as per the module docstring.
'''
+ # detect loops
+ if message.getheader('x-roundup-loop', ''):
+ raise MailLoop
+
# handle the subject line
subject = message.getheader('subject', '')
attachments.append((name, 'message/rfc822', part.fp.read()))
else:
# try name on Content-Type
- name = part.getparam('name')
+ name = part.getparam('name').strip()
+ if not name:
+ disp = part.getheader('content-disposition', None)
+ if disp:
+ name = disp.getparam('filename').strip()
# this is just an attachment
data = self.get_part_data_decoded(part)
attachments.append((name, part.gettype(), data))
content = self.get_part_data_decoded(message)
# figure how much we should muck around with the email body
- keep_citations = getattr(self.instance, 'EMAIL_KEEP_QUOTED_TEXT',
+ keep_citations = getattr(self.instance.config, 'EMAIL_KEEP_QUOTED_TEXT',
'no') == 'yes'
- keep_body = getattr(self.instance, 'EMAIL_LEAVE_BODY_UNCHANGED',
+ keep_body = getattr(self.instance.config, 'EMAIL_LEAVE_BODY_UNCHANGED',
'no') == 'yes'
# parse the body of the message, stripping out bits as appropriate
# try the user alternate addresses if possible
props = db.user.getprops()
if props.has_key('alternate_addresses'):
- users = db.user.filter(None, {'alternate_addresses': address},
- [], [])
+ users = db.user.filter(None, {'alternate_addresses': address})
user = extractUserFromList(db.user, users)
if user is not None: return user
l.append(section)
continue
# keep this section - it has reponse stuff in it
- if not summary:
- # and while we're at it, use the first non-quoted bit as
- # our summary
- summary = line
lines = lines[lines.index(line):]
section = '\n'.join(lines)
+ # and while we're at it, use the first non-quoted bit as
+ # our summary
+ summary = section
if not summary:
# if we don't have our summary yet use the first line of this
# section
- summary = lines[0]
+ summary = section
elif signature.match(lines[0]) and 2 <= len(lines) <= 10:
# lose any signature
break
# and add the section to the output
l.append(section)
+ # figure the summary - find the first sentence-ending punctuation or the
+ # first whole line, whichever is longest
+ sentence = re.search(r'^([^!?\.]+[!?\.])', summary)
+ if sentence:
+ sentence = sentence.group(1)
+ else:
+ sentence = ''
+ first = eol.split(summary)[0]
+ summary = max(sentence, first)
+
# Now reconstitute the message content minus the bits we don't care
# about.
if not keep_body: