diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index 26b66dcd4d6f8f7a52bffd68ad409eb83a4502d9..726cee0929918a0e442b22c3b97ead985b2436b4 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.84 2002-09-10 02:37:27 richard Exp $
+$Id: mailgw.py,v 1.92 2002-09-26 03:03:18 richard Exp $
'''
import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri
-import time, random
+import time, random, sys
import traceback, MimeWriter
import hyperdb, date, password
return Message(s)
subject_re = re.compile(r'(?P<refwd>\s*\W?\s*(fwd|re|aw)\s*\W?\s*)*'
- r'\s*(\[(?P<classname>[^\d\s]+)(?P<nodeid>\d+)?\])?'
- r'\s*(?P<title>[^[]+)?(\[(?P<args>.+?)\])?', re.I)
+ r'\s*(?P<quote>")?(\[(?P<classname>[^\d\s]+)(?P<nodeid>\d+)?\])?'
+ r'\s*(?P<title>[^[]+)?"?(\[(?P<args>.+?)\])?', re.I)
class MailGW:
def __init__(self, instance, db):
# (for testing)
self.trapExceptions = 1
+ def do_pipe(self):
+ ''' Read a message from standard input and pass it to the mail handler.
+ '''
+ self.main(sys.stdin)
+ return 0
+
+ def do_mailbox(self, filename):
+ ''' Read a series of messages from the specified unix mailbox file and
+ pass each to the mail handler.
+ '''
+ # open the spool file and lock it
+ import fcntl, FCNTL
+ f = open(filename, 'r+')
+ fcntl.flock(f.fileno(), FCNTL.LOCK_EX)
+
+ # handle and clear the mailbox
+ try:
+ from mailbox import UnixMailbox
+ mailbox = UnixMailbox(f, factory=Message)
+ # grab one message
+ message = mailbox.next()
+ while message:
+ # handle this message
+ self.handle_Message(message)
+ message = mailbox.next()
+ # nuke the file contents
+ os.ftruncate(f.fileno(), 0)
+ except:
+ import traceback
+ traceback.print_exc()
+ return 1
+ fcntl.flock(f.fileno(), FCNTL.LOCK_UN)
+ return 0
+
+ def do_pop(self, server, user='', password=''):
+ '''Read a series of messages from the specified POP server.
+ '''
+ import getpass, poplib, socket
+ try:
+ if not user:
+ user = raw_input(_('User: '))
+ if not password:
+ password = getpass.getpass()
+ except (KeyboardInterrupt, EOFError):
+ # Ctrl C or D maybe also Ctrl Z under Windows.
+ print "\nAborted by user."
+ return 1
+
+ # open a connection to the server and retrieve all messages
+ try:
+ server = poplib.POP3(server)
+ except socket.error, message:
+ print "POP server error:", message
+ return 1
+ server.user(user)
+ server.pass_(password)
+ numMessages = len(server.list()[1])
+ for i in range(1, numMessages+1):
+ # retr: returns
+ # [ pop response e.g. '+OK 459 octets',
+ # [ array of message lines ],
+ # number of octets ]
+ lines = server.retr(i)[1]
+ s = cStringIO.StringIO('\n'.join(lines))
+ s.seek(0)
+ self.handle_Message(Message(s))
+ # delete the message
+ server.dele(i)
+
+ # quit the server to commit changes.
+ server.quit()
+ return 0
+
def main(self, fp):
''' fp - the file from which to read the Message.
'''
msg = cStringIO.StringIO()
writer = MimeWriter.MimeWriter(msg)
writer.addheader('Subject', subject)
- writer.addheader('From', '%s <%s>'% (self.instance.config.INSTANCE_NAME,
- self.instance.config.ISSUE_TRACKER_EMAIL))
+ writer.addheader('From', '%s <%s>'% (self.instance.config.TRACKER_NAME,
+ self.instance.config.TRACKER_EMAIL))
writer.addheader('To', ','.join(sendto))
writer.addheader('MIME-Version', '1.0')
part = writer.startmultipartbody('mixed')
else:
title = ''
+ # strip off the quotes that dumb emailers put around the subject, like
+ # Re: "[issue1] bla blah"
+ if m.group('quote') and title.endswith('"'):
+ title = title[:-1]
+
# but we do need either a title or a nodeid...
if nodeid is None and not title:
raise MailUsageError, '''
Subject was: "%s"
'''%(nodeid, subject)
+ #
+ # handle the users
+ #
+ # Don't create users if anonymous isn't allowed to register
+ create = 1
+ anonid = self.db.user.lookup('anonymous')
+ if not self.db.security.hasPermission('Email Registration', anonid):
+ create = 0
+
+ # ok, now figure out who the author is - create a new user if the
+ # "create" flag is true
+ author = uidFromAddress(self.db, message.getaddrlist('from')[0],
+ create=create)
+
+ # no author? means we're not author
+ if not author:
+ raise Unauthorized, '''
+You are not a registered user.
+
+Unknown address: %s
+'''%message.getaddrlist('from')[0][1]
+
+ # make sure the author has permission to use the email interface
+ if not self.db.security.hasPermission('Email Access', author):
+ raise Unauthorized, 'You are not permitted to access this tracker.'
+
+ # make sure they're allowed to edit this class of information
+ if not self.db.security.hasPermission('Edit', author, classname):
+ raise Unauthorized, 'You are not permitted to edit %s.'%classname
+
+ # the author may have been created - make sure the change is
+ # committed before we reopen the database
+ self.db.commit()
+
+ # reopen the database as the author
+ username = self.db.user.get(author, 'username')
+ self.db.close()
+ self.db = self.instance.open(username)
+
+ # re-get the class with the new database connection
+ cl = self.db.getclass(classname)
+
+ # now update the recipients list
+ recipients = []
+ tracker_email = self.instance.config.TRACKER_EMAIL.lower()
+ for recipient in message.getaddrlist('to') + message.getaddrlist('cc'):
+ r = recipient[1].strip().lower()
+ if r == tracker_email or not r:
+ continue
+
+ # look up the recipient - create if necessary (and we're
+ # allowed to)
+ recipient = uidFromAddress(self.db, recipient, create)
+
+ # if all's well, add the recipient to the list
+ if recipient:
+ recipients.append(recipient)
+
#
# extract the args
#
Subject was: "%s"
'''%(errors, subject)
- #
- # handle the users
- #
-
- # Don't create users if anonymous isn't allowed to register
- create = 1
- anonid = self.db.user.lookup('anonymous')
- if not self.db.security.hasPermission('Email Registration', anonid):
- create = 0
-
- # ok, now figure out who the author is - create a new user if the
- # "create" flag is true
- author = uidFromAddress(self.db, message.getaddrlist('from')[0],
- create=create)
-
- # no author? means we're not author
- if not author:
- raise Unauthorized, '''
-You are not a registered user.
-
-Unknown address: %s
-'''%message.getaddrlist('from')[0][1]
-
- # make sure the author has permission to use the email interface
- if not self.db.security.hasPermission('Email Access', author):
- raise Unauthorized, 'You are not permitted to access this tracker.'
-
- # the author may have been created - make sure the change is
- # committed before we reopen the database
- self.db.commit()
-
- # reopen the database as the author
- username = self.db.user.get(author, 'username')
- self.db = self.instance.open(username)
-
- # re-get the class with the new database connection
- cl = self.db.getclass(classname)
-
- # now update the recipients list
- recipients = []
- tracker_email = self.instance.config.ISSUE_TRACKER_EMAIL.lower()
- for recipient in message.getaddrlist('to') + message.getaddrlist('cc'):
- r = recipient[1].strip().lower()
- if r == tracker_email or not r:
- continue
-
- # look up the recipient - create if necessary (and we're
- # allowed to)
- recipient = uidFromAddress(self.db, recipient, create)
-
- # if all's well, add the recipient to the list
- if recipient:
- recipients.append(recipient)
-
#
# handle message-id and in-reply-to
#
signature=re.compile(r'^[>|\s]*[-_]+\s*$'),
original_message=re.compile(r'^[>|\s]*-----Original Message-----$')):
''' The message body is divided into sections by blank lines.
- Sections where the second and all subsequent lines begin with a ">" or "|"
- character are considered "quoting sections". The first line of the first
- non-quoting section becomes the summary of the message.
+ Sections where the second and all subsequent lines begin with a ">"
+ or "|" character are considered "quoting sections". The first line of
+ the first non-quoting section becomes the summary of the message.
+
+ If keep_citations is true, then we keep the "quoting sections" in the
+ content.
+ If keep_body is true, we even keep the signature sections.
'''
# strip off leading carriage-returns / newlines
i = 0
# see if there's a response somewhere inside this section (ie.
# no blank line between quoted message and response)
for line in lines[1:]:
- if line[0] not in '>|':
+ if line and line[0] not in '>|':
break
else:
# we keep quoted bits if specified in the config
# and add the section to the output
l.append(section)
- # we only set content for those who want to delete cruft from the
- # message body, otherwise the body is left untouched.
+
+ # Now reconstitute the message content minus the bits we don't care
+ # about.
if not keep_body:
content = '\n\n'.join(l)
+
return summary, content
# vim: set filetype=python ts=4 sw=4 et si