comparison roundup/mailgw.py @ 5418:55f09ca366c4

Python 3 preparation: StringIO. This generally arranges for StringIO and cStringIO references to use io.StringIO for Python 3 but io.BytesIO for Python 2, consistent with the string representations generally used in Roundup. A special FasterStringIO in the TAL code, which referenced internals of the old Python 2 StringIO module, is cut down so it doesn't actually do anything beyond the StringIO class it inherits from (it would also be reasonable to remove FasterStringIO completely). One place in roundup_server.py clearly needing binary I/O is made to use io.BytesIO unconditionally.
author Joseph Myers <jsm@polyomino.org.uk>
date Wed, 25 Jul 2018 09:08:29 +0000
parents 56c9bcdea47f
children 86b6cea7a975
comparison
equal deleted inserted replaced
5417:c749d6795bc2 5418:55f09ca366c4
93 explanatory message given in the exception. 93 explanatory message given in the exception.
94 """ 94 """
95 from __future__ import print_function 95 from __future__ import print_function
96 __docformat__ = 'restructuredtext' 96 __docformat__ = 'restructuredtext'
97 97
98 import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri 98 import string, re, os, mimetools, smtplib, socket, binascii, quopri
99 import time, random, sys, logging 99 import time, random, sys, logging
100 import codecs 100 import codecs
101 import traceback 101 import traceback
102 import email.utils 102 import email.utils
103 103
106 106
107 from roundup import configuration, hyperdb, date, password, exceptions 107 from roundup import configuration, hyperdb, date, password, exceptions
108 from roundup.mailer import Mailer, MessageSendError 108 from roundup.mailer import Mailer, MessageSendError
109 from roundup.i18n import _ 109 from roundup.i18n import _
110 from roundup.hyperdb import iter_roles 110 from roundup.hyperdb import iter_roles
111 from roundup.anypy.strings import StringIO
111 112
112 try: 113 try:
113 import pyme, pyme.core, pyme.constants, pyme.constants.sigsum 114 import pyme, pyme.core, pyme.constants, pyme.constants.sigsum
114 except ImportError: 115 except ImportError:
115 pyme = None 116 pyme = None
221 ''' Get a single part of a multipart message and return it as a new 222 ''' Get a single part of a multipart message and return it as a new
222 Message instance. 223 Message instance.
223 ''' 224 '''
224 boundary = self.getparam('boundary') 225 boundary = self.getparam('boundary')
225 mid, end = '--'+boundary, '--'+boundary+'--' 226 mid, end = '--'+boundary, '--'+boundary+'--'
226 s = cStringIO.StringIO() 227 s = StringIO()
227 while 1: 228 while 1:
228 line = self.fp.readline() 229 line = self.fp.readline()
229 if not line: 230 if not line:
230 break 231 break
231 if line.strip() in (mid, end): 232 if line.strip() in (mid, end):
301 if self.gettype() == 'message/rfc822': 302 if self.gettype() == 'message/rfc822':
302 # handle message/rfc822 specially - the name should be 303 # handle message/rfc822 specially - the name should be
303 # the subject of the actual e-mail embedded here 304 # the subject of the actual e-mail embedded here
304 # we add a '.eml' extension like other email software does it 305 # we add a '.eml' extension like other email software does it
305 self.fp.seek(0) 306 self.fp.seek(0)
306 s = cStringIO.StringIO(self.getbody()) 307 s = StringIO(self.getbody())
307 name = Message(s).getheader('subject') 308 name = Message(s).getheader('subject')
308 if name: 309 if name:
309 name = name + '.eml' 310 name = name + '.eml'
310 if not name: 311 if not name:
311 # try name on Content-Type 312 # try name on Content-Type
327 # BUG: is base64 really used for text encoding or 328 # BUG: is base64 really used for text encoding or
328 # are we inserting zip files here. 329 # are we inserting zip files here.
329 data = binascii.a2b_base64(self.fp.read()) 330 data = binascii.a2b_base64(self.fp.read())
330 elif encoding == 'quoted-printable': 331 elif encoding == 'quoted-printable':
331 # the quopri module wants to work with files 332 # the quopri module wants to work with files
332 decoded = cStringIO.StringIO() 333 decoded = StringIO()
333 quopri.decode(self.fp, decoded) 334 quopri.decode(self.fp, decoded)
334 data = decoded.getvalue() 335 data = decoded.getvalue()
335 elif encoding == 'uuencoded': 336 elif encoding == 'uuencoded':
336 data = binascii.a2b_uu(self.fp.read()) 337 data = binascii.a2b_uu(self.fp.read())
337 else: 338 else:
447 attachments.extend(new_attach) 448 attachments.extend(new_attach)
448 if ig and content_type == 'multipart/alternative' and content: 449 if ig and content_type == 'multipart/alternative' and content:
449 attachments = [] 450 attachments = []
450 html_part = False 451 html_part = False
451 elif unpack_rfc822 and content_type == 'message/rfc822': 452 elif unpack_rfc822 and content_type == 'message/rfc822':
452 s = cStringIO.StringIO(self.getbody()) 453 s = StringIO(self.getbody())
453 m = Message(s) 454 m = Message(s)
454 ig = ignore_alternatives and not content 455 ig = ignore_alternatives and not content
455 new_content, attachments, html_part = m.extract_content(m.gettype(), ig, 456 new_content, attachments, html_part = m.extract_content(m.gettype(), ig,
456 unpack_rfc822, html2text) 457 unpack_rfc822, html2text)
457 attachments.insert(0, m.text_as_attachment()) 458 attachments.insert(0, m.text_as_attachment())
525 526
526 plaintext.seek(0,0) 527 plaintext.seek(0,0)
527 # pyme.core.Data implements a seek method with a different signature 528 # pyme.core.Data implements a seek method with a different signature
528 # than roundup can handle. So we'll put the data in a container that 529 # than roundup can handle. So we'll put the data in a container that
529 # the Message class can work with. 530 # the Message class can work with.
530 c = cStringIO.StringIO() 531 c = StringIO()
531 c.write(plaintext.read()) 532 c.write(plaintext.read())
532 c.seek(0) 533 c.seek(0)
533 return Message(c) 534 return Message(c)
534 535
535 def verify_signature(self, author): 536 def verify_signature(self, author):
1326 Read into an internal structure that we can seek on (in case 1327 Read into an internal structure that we can seek on (in case
1327 there's an error). 1328 there's an error).
1328 1329
1329 XXX: we may want to read this into a temporary file instead... 1330 XXX: we may want to read this into a temporary file instead...
1330 """ 1331 """
1331 s = cStringIO.StringIO() 1332 s = StringIO()
1332 s.write(sys.stdin.read()) 1333 s.write(sys.stdin.read())
1333 s.seek(0) 1334 s.seek(0)
1334 self.main(s) 1335 self.main(s)
1335 return 0 1336 return 0
1336 1337
1422 1423
1423 # mark the message as deleted. 1424 # mark the message as deleted.
1424 server.store(str(i), '+FLAGS', r'(\Deleted)') 1425 server.store(str(i), '+FLAGS', r'(\Deleted)')
1425 1426
1426 # process the message 1427 # process the message
1427 s = cStringIO.StringIO(data[0][1]) 1428 s = StringIO(data[0][1])
1428 s.seek(0) 1429 s.seek(0)
1429 self.handle_Message(Message(s)) 1430 self.handle_Message(Message(s))
1430 server.close() 1431 server.close()
1431 finally: 1432 finally:
1432 try: 1433 try:
1491 # retr: returns 1492 # retr: returns
1492 # [ pop response e.g. '+OK 459 octets', 1493 # [ pop response e.g. '+OK 459 octets',
1493 # [ array of message lines ], 1494 # [ array of message lines ],
1494 # number of octets ] 1495 # number of octets ]
1495 lines = server.retr(i)[1] 1496 lines = server.retr(i)[1]
1496 s = cStringIO.StringIO('\n'.join(lines)) 1497 s = StringIO('\n'.join(lines))
1497 s.seek(0) 1498 s.seek(0)
1498 self.handle_Message(Message(s)) 1499 self.handle_Message(Message(s))
1499 # delete the message 1500 # delete the message
1500 server.dele(i) 1501 server.dele(i)
1501 1502

Roundup Issue Tracker: http://roundup-tracker.org/