X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=roundup%2Fmailgw.py;h=3898051e4aa5b30229f4f77611126b3d190e9aa5;hb=3e236cbcab0a816b8eabfafe59443c8aefd5cc2c;hp=87a685cbe17eb77b05a8327d5f3b7839218ad371;hpb=b8500763eee3291858a0012715684796445243bb;p=roundup.git diff --git a/roundup/mailgw.py b/roundup/mailgw.py index 87a685c..3898051 100644 --- a/roundup/mailgw.py +++ b/roundup/mailgw.py @@ -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.59 2002-01-23 21:43:23 richard Exp $ +$Id: mailgw.py,v 1.65 2002-02-15 00:13:38 richard Exp $ ''' @@ -120,7 +120,7 @@ class Message(mimetools.Message): return Message(s) subject_re = re.compile(r'(?P\s*\W?\s*(fwd|re|aw)\s*\W?\s*)*' - r'\s*(\[(?P[^\d\s]+)(?P\d+)?\])' + r'\s*(\[(?P[^\d\s]+)(?P\d+)?\])?' r'\s*(?P[^[]+)?(\[(?P<args>.+?)\])?', re.I) class MailGW: @@ -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 @@ -273,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 @@ -288,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: @@ -425,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: @@ -474,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() @@ -486,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 @@ -500,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 @@ -534,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 @@ -549,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) # @@ -757,6 +803,24 @@ 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 #