Skip to content

Commit 92c7ea3

Browse files
author
Engelbert Gruber
committed
Clean up mail handling, multipart handling.
1 parent ba83844 commit 92c7ea3

File tree

3 files changed

+68
-57
lines changed

3 files changed

+68
-57
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ are given with the most recent entry first.
33

44
2002-02-?? - ?????
55
Fixed:
6+
. Clean up mail handling, multipart handling.
67
. respect encodings in non multipart messages.
78
. makeHtmlBase: re.sub under python 2.2 did not replace '.', string.replace
89
does it.

roundup/mailgw.py

Lines changed: 54 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class node. Any parts of other types are each stored in separate files
7373
an exception, the original message is bounced back to the sender with the
7474
explanatory message given in the exception.
7575
76-
$Id: mailgw.py,v 1.62 2002-02-05 14:15:29 grubert Exp $
76+
$Id: mailgw.py,v 1.63 2002-02-12 08:08:55 grubert Exp $
7777
'''
7878

7979

@@ -261,6 +261,25 @@ def bounce_message(self, message, sendto, error,
261261
writer.lastpart()
262262
return msg
263263

264+
def get_part_data_decoded(self,part):
265+
encoding = part.getencoding()
266+
data = None
267+
if encoding == 'base64':
268+
# BUG: is base64 really used for text encoding or
269+
# are we inserting zip files here.
270+
data = binascii.a2b_base64(part.fp.read())
271+
elif encoding == 'quoted-printable':
272+
# the quopri module wants to work with files
273+
decoded = cStringIO.StringIO()
274+
quopri.decode(part.fp, decoded)
275+
data = decoded.getvalue()
276+
elif encoding == 'uuencoded':
277+
data = binascii.a2b_uu(part.fp.read())
278+
else:
279+
# take it as text
280+
data = part.fp.read()
281+
return data
282+
264283
def handle_message(self, message):
265284
''' message - a Message instance
266285
@@ -474,6 +493,32 @@ def handle_message(self, message):
474493
#
475494
content_type = message.gettype()
476495
attachments = []
496+
# General multipart handling:
497+
# Take the first text/plain part, anything else is considered an
498+
# attachment.
499+
# multipart/mixed: multiple "unrelated" parts.
500+
# multipart/signed (rfc 1847):
501+
# The control information is carried in the second of the two
502+
# required body parts.
503+
# ACTION: Default, so if content is text/plain we get it.
504+
# multipart/encrypted (rfc 1847):
505+
# The control information is carried in the first of the two
506+
# required body parts.
507+
# ACTION: Not handleable as the content is encrypted.
508+
# multipart/related (rfc 1872, 2112, 2387):
509+
# The Multipart/Related content-type addresses the MIME representation
510+
# of compound objects.
511+
# ACTION: Default. If we are lucky there is a text/plain.
512+
# TODO: One should use the start part and look for an Alternative
513+
# that is text/plain.
514+
# multipart/Alternative (rfc 1872, 1892):
515+
# only in "related" ?
516+
# multipart/report (rfc 1892):
517+
# e.g. mail system delivery status reports.
518+
# ACTION: Default. Could be ignored or used for Delivery Notification
519+
# flagging.
520+
# multipart/form-data:
521+
# For web forms only.
477522
if content_type == 'multipart/mixed':
478523
# skip over the intro to the first boundary
479524
part = message.getPart()
@@ -486,32 +531,8 @@ def handle_message(self, message):
486531
# parse it
487532
subtype = part.gettype()
488533
if subtype == 'text/plain' and not content:
489-
# add all text/plain parts to the message content
490-
# BUG (in code or comment) only add the first one.
491-
if content is None:
492-
# try name on Content-Type
493-
# maybe add name to non text content ?
494-
name = part.getparam('name')
495-
# assume first part is the mail
496-
encoding = part.getencoding()
497-
if encoding == 'base64':
498-
# BUG: is base64 really used for text encoding or
499-
# are we inserting zip files here.
500-
data = binascii.a2b_base64(part.fp.read())
501-
elif encoding == 'quoted-printable':
502-
# the quopri module wants to work with files
503-
decoded = cStringIO.StringIO()
504-
quopri.decode(part.fp, decoded)
505-
data = decoded.getvalue()
506-
elif encoding == 'uuencoded':
507-
data = binascii.a2b_uu(part.fp.read())
508-
else:
509-
# take it as text
510-
data = part.fp.read()
511-
content = data
512-
else:
513-
content = content + part.fp.read()
514-
534+
# The first text/plain part is the message content.
535+
content = self.get_part_data_decoded(part)
515536
elif subtype == 'message/rfc822':
516537
# handle message/rfc822 specially - the name should be
517538
# the subject of the actual e-mail embedded here
@@ -520,21 +541,11 @@ def handle_message(self, message):
520541
name = mailmess.getheader('subject')
521542
part.fp.seek(i)
522543
attachments.append((name, 'message/rfc822', part.fp.read()))
523-
524544
else:
525545
# try name on Content-Type
526546
name = part.getparam('name')
527547
# this is just an attachment
528-
encoding = part.getencoding()
529-
if encoding == 'base64':
530-
data = binascii.a2b_base64(part.fp.read())
531-
elif encoding == 'quoted-printable':
532-
# the quopri module wants to work with files
533-
decoded = cStringIO.StringIO()
534-
quopri.decode(part.fp, decoded)
535-
data = decoded.getvalue()
536-
elif encoding == 'uuencoded':
537-
data = binascii.a2b_uu(part.fp.read())
548+
data = self.get_part_data_decoded(part)
538549
attachments.append((name, part.gettype(), data))
539550
if content is None:
540551
raise MailUsageError, '''
@@ -553,8 +564,7 @@ def handle_message(self, message):
553564
break
554565
# parse it
555566
if part.gettype() == 'text/plain' and not content:
556-
# this one's our content
557-
content = part.fp.read()
567+
content = self.get_part_data_decoded(part)
558568
if content is None:
559569
raise MailUsageError, '''
560570
Roundup requires the submission to be plain text. The message parser could
@@ -568,22 +578,7 @@ def handle_message(self, message):
568578
'''
569579

570580
else:
571-
encoding = message.getencoding()
572-
if encoding == 'base64':
573-
# BUG: is base64 really used for text encoding or
574-
# are we inserting zip files here.
575-
data = binascii.a2b_base64(message.fp.read())
576-
elif encoding == 'quoted-printable':
577-
# the quopri module wants to work with files
578-
decoded = cStringIO.StringIO()
579-
quopri.decode(message.fp, decoded)
580-
data = decoded.getvalue()
581-
elif encoding == 'uuencoded':
582-
data = binascii.a2b_uu(message.fp.read())
583-
else:
584-
# take it as text
585-
data = message.fp.read()
586-
content = data
581+
content = self.get_part_data_decoded(message)
587582

588583
summary, content = parseContent(content)
589584

@@ -791,6 +786,9 @@ def parseContent(content, blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'),
791786

792787
#
793788
# $Log: not supported by cvs2svn $
789+
# Revision 1.62 2002/02/05 14:15:29 grubert
790+
# . respect encodings in non multipart messages.
791+
#
794792
# Revision 1.61 2002/02/04 09:40:21 grubert
795793
# . add test for multipart messages with first part being encoded.
796794
#

test/test_mailgw.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# but WITHOUT ANY WARRANTY; without even the implied warranty of
99
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1010
#
11-
# $Id: test_mailgw.py,v 1.9 2002-02-05 14:15:29 grubert Exp $
11+
# $Id: test_mailgw.py,v 1.10 2002-02-12 08:08:55 grubert Exp $
1212

1313
import unittest, cStringIO, tempfile, os, shutil, errno, imp, sys
1414

@@ -105,6 +105,15 @@ def testNewIssueAuthMsg(self):
105105
___________________________________________________
106106
''')
107107

108+
# BUG
109+
# def testMultipart(self):
110+
# '''With more than one part'''
111+
# see MultipartEnc tests: but if there is more than one part
112+
# we return a multipart/mixed and the boundary contains
113+
# the ip address of the test machine.
114+
115+
# BUG should test some binary attamchent too.
116+
108117
def testFollowup(self):
109118
self.testNewIssue()
110119
message = cStringIO.StringIO('''Content-Type: text/plain;
@@ -296,6 +305,9 @@ def suite():
296305

297306
#
298307
# $Log: not supported by cvs2svn $
308+
# Revision 1.9 2002/02/05 14:15:29 grubert
309+
# . respect encodings in non multipart messages.
310+
#
299311
# Revision 1.8 2002/02/04 09:40:21 grubert
300312
# . add test for multipart messages with first part being encoded.
301313
#

0 commit comments

Comments
 (0)