Code

. Clean up mail handling, multipart handling.
authorgrubert <grubert@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 12 Feb 2002 08:08:55 +0000 (08:08 +0000)
committergrubert <grubert@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 12 Feb 2002 08:08:55 +0000 (08:08 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@621 57a73879-2fb5-44c3-a270-3262357dd7e2

CHANGES.txt
roundup/mailgw.py
test/test_mailgw.py

index ed3577d180a11c1b1aff3b72ac98c6777c8abc21..bd3deaac961160fc271ed5bd863bdc5549577a5f 100644 (file)
@@ -3,6 +3,7 @@ are given with the most recent entry first.
 
 2002-02-?? - ?????
 Fixed:
+ . Clean up mail handling, multipart handling.
  . respect encodings in non multipart messages.
  . makeHtmlBase: re.sub under python 2.2 did not replace '.', string.replace
    does it.
index b624b1bc6f560e3cc6db156344d38166c9bd7855..9a3083adfd49b628a1ab9fb5c064fc08638be81e 100644 (file)
@@ -73,7 +73,7 @@ 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.62 2002-02-05 14:15:29 grubert Exp $
+$Id: mailgw.py,v 1.63 2002-02-12 08:08:55 grubert Exp $
 '''
 
 
@@ -261,6 +261,25 @@ class MailGW:
         writer.lastpart()
         return msg
 
+    def get_part_data_decoded(self,part):
+        encoding = part.getencoding()
+        data = None
+        if encoding == 'base64':
+            # BUG: is base64 really used for text encoding or
+            # are we inserting zip files here. 
+            data = binascii.a2b_base64(part.fp.read())
+        elif encoding == 'quoted-printable':
+            # the quopri module wants to work with files
+            decoded = cStringIO.StringIO()
+            quopri.decode(part.fp, decoded)
+            data = decoded.getvalue()
+        elif encoding == 'uuencoded':
+            data = binascii.a2b_uu(part.fp.read())
+        else:
+            # take it as text
+            data = part.fp.read()
+        return data
+
     def handle_message(self, message):
         ''' message - a Message instance
 
@@ -474,6 +493,32 @@ Unknown address: %s
         #
         content_type =  message.gettype()
         attachments = []
+        # General multipart handling:
+        #   Take the first text/plain part, anything else is considered an 
+        #   attachment.
+        # multipart/mixed: multiple "unrelated" parts.
+        # multipart/signed (rfc 1847): 
+        #   The control information is carried in the second of the two 
+        #   required body parts.
+        #   ACTION: Default, so if content is text/plain we get it.
+        # multipart/encrypted (rfc 1847): 
+        #   The control information is carried in the first of the two 
+        #   required body parts.
+        #   ACTION: Not handleable as the content is encrypted.
+        # multipart/related (rfc 1872, 2112, 2387):
+        #   The Multipart/Related content-type addresses the MIME representation         
+        #   of compound objects.
+        #   ACTION: Default. If we are lucky there is a text/plain.
+        #   TODO: One should use the start part and look for an Alternative
+        #   that is text/plain.
+        # multipart/Alternative (rfc 1872, 1892):
+        #   only in "related" ?
+        # multipart/report (rfc 1892):
+        #   e.g. mail system delivery status reports.
+        #   ACTION: Default. Could be ignored or used for Delivery Notification 
+        #   flagging.
+        # multipart/form-data:
+        #   For web forms only.
         if content_type == 'multipart/mixed':
             # skip over the intro to the first boundary
             part = message.getPart()
@@ -486,32 +531,8 @@ Unknown address: %s
                 # parse it
                 subtype = part.gettype()
                 if subtype == 'text/plain' and not content:
-                    # add all text/plain parts to the message content
-                    # BUG (in code or comment) only add the first one. 
-                    if content is None:
-                        # try name on Content-Type
-                        # maybe add name to non text content ?
-                        name = part.getparam('name')
-                        # assume first part is the mail
-                        encoding = part.getencoding()
-                        if encoding == 'base64':
-                            # BUG: is base64 really used for text encoding or
-                            # are we inserting zip files here. 
-                            data = binascii.a2b_base64(part.fp.read())
-                        elif encoding == 'quoted-printable':
-                            # the quopri module wants to work with files
-                            decoded = cStringIO.StringIO()
-                            quopri.decode(part.fp, decoded)
-                            data = decoded.getvalue()
-                        elif encoding == 'uuencoded':
-                            data = binascii.a2b_uu(part.fp.read())
-                        else:
-                            # take it as text
-                            data = part.fp.read()
-                        content = data
-                    else:
-                        content = content + part.fp.read()
-
+                    # The first text/plain part is the message content.
+                    content = self.get_part_data_decoded(part) 
                 elif subtype == 'message/rfc822':
                     # handle message/rfc822 specially - the name should be
                     # the subject of the actual e-mail embedded here
@@ -520,21 +541,11 @@ Unknown address: %s
                     name = mailmess.getheader('subject')
                     part.fp.seek(i)
                     attachments.append((name, 'message/rfc822', part.fp.read()))
-
                 else:
                     # try name on Content-Type
                     name = part.getparam('name')
                     # this is just an attachment
-                    encoding = part.getencoding()
-                    if encoding == 'base64':
-                        data = binascii.a2b_base64(part.fp.read())
-                    elif encoding == 'quoted-printable':
-                        # the quopri module wants to work with files
-                        decoded = cStringIO.StringIO()
-                        quopri.decode(part.fp, decoded)
-                        data = decoded.getvalue()
-                    elif encoding == 'uuencoded':
-                        data = binascii.a2b_uu(part.fp.read())
+                    data = self.get_part_data_decoded(part) 
                     attachments.append((name, part.gettype(), data))
             if content is None:
                 raise MailUsageError, '''
@@ -553,8 +564,7 @@ not find a text/plain part to use.
                     break
                 # parse it
                 if part.gettype() == 'text/plain' and not content:
-                    # this one's our content
-                    content = part.fp.read()
+                    content = self.get_part_data_decoded(part) 
             if content is None:
                 raise MailUsageError, '''
 Roundup requires the submission to be plain text. The message parser could
@@ -568,22 +578,7 @@ not find a text/plain part to use.
 '''
 
         else:
-            encoding = message.getencoding()
-            if encoding == 'base64':
-                # BUG: is base64 really used for text encoding or
-                # are we inserting zip files here. 
-                data = binascii.a2b_base64(message.fp.read())
-            elif encoding == 'quoted-printable':
-                # the quopri module wants to work with files
-                decoded = cStringIO.StringIO()
-                quopri.decode(message.fp, decoded)
-                data = decoded.getvalue()
-            elif encoding == 'uuencoded':
-                data = binascii.a2b_uu(message.fp.read())
-            else:
-                # take it as text
-                data = message.fp.read()
-            content = data
+            content = self.get_part_data_decoded(message) 
  
         summary, content = parseContent(content)
 
@@ -791,6 +786,9 @@ def parseContent(content, blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'),
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.62  2002/02/05 14:15:29  grubert
+#  . respect encodings in non multipart messages.
+#
 # Revision 1.61  2002/02/04 09:40:21  grubert
 #  . add test for multipart messages with first part being encoded.
 #
index 382c8f27805ad2e7161389bb5daf3ac080458998..e1a750858d01bd863cfe125a2c59c8abc4053db4 100644 (file)
@@ -8,7 +8,7 @@
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 #
-# $Id: test_mailgw.py,v 1.9 2002-02-05 14:15:29 grubert Exp $
+# $Id: test_mailgw.py,v 1.10 2002-02-12 08:08:55 grubert Exp $
 
 import unittest, cStringIO, tempfile, os, shutil, errno, imp, sys
 
@@ -105,6 +105,15 @@ http://some.useful.url/issue1
 ___________________________________________________
 ''')
 
+    # BUG
+    # def testMultipart(self):
+    #  '''With more than one part'''
+    #  see MultipartEnc tests: but if there is more than one part
+    #  we return a multipart/mixed and the boundary contains
+    #  the ip address of the test machine. 
+
+    # BUG should test some binary attamchent too.
+       
     def testFollowup(self):
         self.testNewIssue()
         message = cStringIO.StringIO('''Content-Type: text/plain;
@@ -296,6 +305,9 @@ def suite():
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.9  2002/02/05 14:15:29  grubert
+#  . respect encodings in non multipart messages.
+#
 # Revision 1.8  2002/02/04 09:40:21  grubert
 #  . add test for multipart messages with first part being encoded.
 #