Code

#614188 ] Exception in mailgw.py
[roundup.git] / roundup / mailgw.py
index 26b66dcd4d6f8f7a52bffd68ad409eb83a4502d9..90e98afaef9c31db11370b63ccec2d2dcb4a5ad0 100644 (file)
@@ -73,11 +73,11 @@ are calling the create() method to create a new node). If an auditor raises
 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.90 2002-09-25 05:13:34 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
 
@@ -143,6 +143,79 @@ class MailGW:
         # (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.
         '''
@@ -239,8 +312,8 @@ class MailGW:
         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')
@@ -391,6 +464,64 @@ does not exist.
 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
         #
@@ -521,60 +652,6 @@ There were problems handling your subject line argument list:
 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
         #
@@ -792,9 +869,13 @@ def parseContent(content, keep_citations, keep_body,
         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
@@ -819,7 +900,7 @@ def parseContent(content, keep_citations, keep_body,
             # 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
@@ -847,10 +928,12 @@ def parseContent(content, keep_citations, keep_body,
 
         # 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