Code

Ran it through pychecker, made fixes
[roundup.git] / roundup / mailgw.py
index a47db26c0a6e6348a891974ac973aa2c38bf20f1..3898051e4aa5b30229f4f77611126b3d190e9aa5 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.55 2002-01-21 10:05:47 rochecompaan Exp $
+$Id: mailgw.py,v 1.65 2002-02-15 00:13:38 richard Exp $
 '''
 
 
@@ -119,8 +119,8 @@ class Message(mimetools.Message):
         s.seek(0)
         return Message(s)
 
-subject_re = re.compile(r'(?P<refwd>\s*\W?\s*(fwd|re)\s*\W?\s*)*'
-    r'\s*(\[(?P<classname>[^\d\s]+)(?P<nodeid>\d+)?\])'
+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)
 
 class MailGW:
@@ -174,8 +174,11 @@ class MailGW:
                 m = self.bounce_message(message, sendto, m)
             except:
                 # bounce the message back to the sender with the error message
-                sendto = [sendto[0][1]]
+                sendto = [sendto[0][1], self.instance.ADMIN_EMAIL]
                 m = ['']
+                m.append('An unexpected error occurred during the processing')
+                m.append('of your message. The tracker administrator is being')
+                m.append('notified.\n')
                 m.append('----  traceback of failure  ----')
                 s = cStringIO.StringIO()
                 import traceback
@@ -258,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
 
@@ -270,6 +292,20 @@ class MailGW:
             raise MailUsageHelp
 
         m = subject_re.match(subject)
+
+        # check for well-formed subject line
+        if m:
+            # get the classname
+            classname = m.group('classname')
+            if classname is None:
+                # no classname, fallback on the default
+                if hasattr(self.instance, 'MAIL_DEFAULT_CLASS') and \
+                        self.instance.MAIL_DEFAULT_CLASS:
+                    classname = self.instance.MAIL_DEFAULT_CLASS
+                else:
+                    # fail
+                    m = None
+
         if not m:
             raise MailUsageError, '''
 The message you sent to roundup did not contain a properly formed subject
@@ -285,8 +321,7 @@ line. The subject must contain a class name or designator to indicate the
 Subject was: "%s"
 '''%subject
 
-        # get the classname
-        classname = m.group('classname')
+        # get the class
         try:
             cl = self.db.getclass(classname)
         except KeyError:
@@ -422,11 +457,15 @@ Subject was: "%s"
         # handle the users
         #
 
-        # Don't create users if ANONYMOUS_REGISTER is denied
-        if self.instance.ANONYMOUS_REGISTER == 'deny':
+        # Don't create users if ANONYMOUS_REGISTER_MAIL is denied
+        # ... fall back on ANONYMOUS_REGISTER if the other doesn't exist
+        create = 1
+        if hasattr(self.instance, 'ANONYMOUS_REGISTER_MAIL'):
+            if self.instance.ANONYMOUS_REGISTER_MAIL == 'deny':
+                create = 0
+        elif self.instance.ANONYMOUS_REGISTER == 'deny':
             create = 0
-        else:
-            create = 1
+
         author = self.db.uidFromAddress(message.getaddrlist('from')[0],
             create=create)
         if not author:
@@ -471,6 +510,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()
@@ -483,12 +548,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
-                    if content is None:
-                        content = part.fp.read()
-                    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
@@ -497,23 +558,12 @@ 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, '''
 Roundup requires the submission to be plain text. The message parser could
@@ -531,8 +581,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
@@ -546,8 +595,8 @@ not find a text/plain part to use.
 '''
 
         else:
-            content = message.fp.read()
-
+            content = self.get_part_data_decoded(message) 
         summary, content = parseContent(content)
 
         # 
@@ -580,9 +629,10 @@ not find a text/plain part to use.
                 except KeyError:
                     pass
                 else:
+                    current_status = cl.get(nodeid, 'status')
                     if (not props.has_key('status') and
-                            properties['status'] == unread_id or
-                            properties['status'] == resolved_id):
+                            current_status == unread_id or
+                            current_status == resolved_id):
                         props['status'] = chatting_id
 
             # add nosy in arguments to issue's nosy list
@@ -753,6 +803,43 @@ def parseContent(content, blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'),
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.64  2002/02/14 23:46:02  richard
+# . #516883 ] mail interface + ANONYMOUS_REGISTER
+#
+# Revision 1.63  2002/02/12 08:08:55  grubert
+#  . Clean up mail handling, multipart handling.
+#
+# 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.
+#
+# Revision 1.60  2002/02/01 07:43:12  grubert
+#  . mailgw checks encoding on first part too.
+#
+# Revision 1.59  2002/01/23 21:43:23  richard
+# tabnuke
+#
+# Revision 1.58  2002/01/23 21:41:56  richard
+#  . mailgw failures (unexpected ones) are forwarded to the roundup admin
+#
+# Revision 1.57  2002/01/22 22:27:43  richard
+#  . handle stripping of "AW:" from subject line
+#
+# Revision 1.56  2002/01/22 11:54:45  rochecompaan
+# Fixed status change in mail gateway.
+#
+# Revision 1.55  2002/01/21 10:05:47  rochecompaan
+# Feature:
+#  . the mail gateway now responds with an error message when invalid
+#    values for arguments are specified for link or multilink properties
+#  . modified unit test to check nosy and assignedto when specified as
+#    arguments
+#
+# Fixed:
+#  . fixed setting nosy as argument in subject line
+#
 # Revision 1.54  2002/01/16 09:14:45  grubert
 #  . if the attachment has no name, name it unnamed, happens with tnefs.
 #