Code

Simplify and fix interval comparison.
[roundup.git] / roundup / mailgw.py
index 97848a72ca541d599de85678d85fd32df6c225be..8346b0590cb605ab7a0ac6d4dcba5f1557273f0a 100644 (file)
@@ -27,6 +27,9 @@ Incoming messages are examined for multiple parts:
    and given "file" class nodes that are linked to the "msg" node.
  . In a multipart/alternative message or part, we look for a text/plain
    subpart and ignore the other parts.
+ . A message/rfc822 is treated similar tomultipart/mixed (except for
+   special handling of the first text part) if unpack_rfc822 is set in
+   the mailgw config section.
 
 Summary
 -------
@@ -246,6 +249,10 @@ class Message(mimetools.Message):
 
     def getheader(self, name, default=None):
         hdr = mimetools.Message.getheader(self, name, default)
+        # TODO are there any other False values possible?
+        # TODO if not hdr: return hdr
+        if hdr is None:
+            return None
         if not hdr:
             return ''
         if hdr:
@@ -273,12 +280,17 @@ class Message(mimetools.Message):
 
     def getname(self):
         """Find an appropriate name for this message."""
+        name = None
         if self.gettype() == 'message/rfc822':
             # handle message/rfc822 specially - the name should be
             # the subject of the actual e-mail embedded here
+            # we add a '.eml' extension like other email software does it
             self.fp.seek(0)
-            name = Message(self.fp).getheader('subject')
-        else:
+            s = cStringIO.StringIO(self.getbody())
+            name = Message(s).getheader('subject')
+            if name:
+                name = name + '.eml'
+        if not name:
             # try name on Content-Type
             name = self.getparam('name')
             if not name:
@@ -351,8 +363,11 @@ class Message(mimetools.Message):
     #   flagging.
     # multipart/form-data:
     #   For web forms only.
+    # message/rfc822:
+    #   Only if configured in [mailgw] unpack_rfc822
 
-    def extract_content(self, parent_type=None, ignore_alternatives = False):
+    def extract_content(self, parent_type=None, ignore_alternatives=False,
+        unpack_rfc822=False):
         """Extract the body and the attachments recursively.
 
            If the content is hidden inside a multipart/alternative part,
@@ -370,7 +385,7 @@ class Message(mimetools.Message):
             ig = ignore_alternatives and not content_found
             for part in self.getparts():
                 new_content, new_attach = part.extract_content(content_type,
-                    not content and ig)
+                    not content and ig, unpack_rfc822)
 
                 # If we haven't found a text/plain part yet, take this one,
                 # otherwise make it an attachment.
@@ -395,6 +410,13 @@ class Message(mimetools.Message):
                 attachments.extend(new_attach)
             if ig and content_type == 'multipart/alternative' and content:
                 attachments = []
+        elif unpack_rfc822 and content_type == 'message/rfc822':
+            s = cStringIO.StringIO(self.getbody())
+            m = Message(s)
+            ig = ignore_alternatives and not content
+            new_content, attachments = m.extract_content(m.gettype(), ig,
+                unpack_rfc822)
+            attachments.insert(0, m.text_as_attachment())
         elif (parent_type == 'multipart/signed' and
               content_type == 'application/pgp-signature'):
             # ignore it so it won't be saved as an attachment
@@ -516,7 +538,7 @@ class MailGW:
                 self.default_class = value.strip()
 
         self.mailer = Mailer(instance.config)
-        self.logger = logging.getLogger('mailgw')
+        self.logger = logging.getLogger('roundup.mailgw')
 
         # should we trap exceptions (normal usage) or pass them through
         # (for testing)
@@ -569,7 +591,8 @@ class MailGW:
         fcntl.flock(f.fileno(), FCNTL.LOCK_UN)
         return 0
 
-    def do_imap(self, server, user='', password='', mailbox='', ssl=0):
+    def do_imap(self, server, user='', password='', mailbox='', ssl=0,
+            cram=0):
         ''' Do an IMAP connection
         '''
         import getpass, imaplib, socket
@@ -595,7 +618,10 @@ class MailGW:
             return 1
 
         try:
-            server.login(user, password)
+            if cram:
+                server.login_cram_md5(user, password)
+            else:
+                server.login(user, password)
         except imaplib.IMAP4.error, e:
             self.logger.exception('IMAP login failure')
             return 1
@@ -893,7 +919,7 @@ Emails to Roundup trackers must include a Subject: line!
 
         # if we've not found a valid classname prefix then force the
         # scanning to handle there being a leading delimiter
-        title_re = r'(?P<title>%s[^%s]+)'%(
+        title_re = r'(?P<title>%s[^%s]*)'%(
             not matches['classname'] and '.' or '', delim_open)
         m = re.match(title_re, tmpsubject.strip(), re.IGNORECASE)
         if m:
@@ -1222,6 +1248,9 @@ Subject was: "%(subject)s"
         if (title and properties.has_key('title') and not
                 issue_props.has_key('title')):
             issue_props['title'] = title
+        if (nodeid and properties.has_key('title') and not
+                config['MAILGW_SUBJECT_UPDATES_TITLE']):
+            issue_props['title'] = cl.get(nodeid,'title')
 
         #
         # handle message-id and in-reply-to
@@ -1265,7 +1294,8 @@ This tracker has been configured to require all email be PGP signed or
 encrypted.""")
         # now handle the body - find the message
         ig = self.instance.config.MAILGW_IGNORE_ALTERNATIVES
-        content, attachments = message.extract_content(ignore_alternatives = ig)
+        content, attachments = message.extract_content(ignore_alternatives=ig,
+            unpack_rfc822=self.instance.config.MAILGW_UNPACK_RFC822)
         if content is None:
             raise MailUsageError, _("""
 Roundup requires the submission to be plain text. The message parser could