def setUp(self): self.fp = StringIO() w = self.fp.write w('Content-Type: multipart/mixed; boundary="foo"\r\n\r\n') w('This is a multipart message. Ignore this bit.\r\n') w('\r\n--foo\r\n') w('Content-Type: text/plain\r\n\r\n') w('Hello, world!\r\n') w('\r\n') w('Blah blah\r\n') w('foo\r\n') w('-foo\r\n') w('\r\n--foo\r\n') w('Content-Type: multipart/alternative; boundary="bar"\r\n\r\n') w('This is a multipart message. Ignore this bit.\r\n') w('\r\n--bar\r\n') w('Content-Type: text/plain\r\n\r\n') w('Hello, world!\r\n') w('\r\n') w('Blah blah\r\n') w('\r\n--bar\r\n') w('Content-Type: text/html\r\n\r\n') w('<b>Hello, world!</b>\r\n') w('\r\n--bar--\r\n') w('\r\n--foo\r\n') w('Content-Type: text/plain\r\n\r\n') w('Last bit\n') w('\r\n--foo--\r\n') self.fp.seek(0)
def test_Version_Check(self): # test for valid versions from roundup.version_check import VERSION_NEEDED self.assertEqual((2, 7), VERSION_NEEDED) del (sys.modules['roundup.version_check']) # fake an invalid version real_ver = sys.version_info sys.version_info = (2, 1) # exit is called on failure, but that breaks testing so # just return and discard the exit code. real_exit = sys.exit sys.exit = lambda code: code # error case uses print(), capture and check capturedOutput = StringIO() sys.stdout = capturedOutput from roundup.version_check import VERSION_NEEDED sys.stdout = sys.__stdout__ self.assertIn("Roundup requires Python 2.7", capturedOutput.getvalue()) # reset to valid values for future tests sys.exit = real_exit sys.version_info = real_ver
def gen_message(spec): """Create a basic MIME message according to 'spec'. Each line of a spec has one content-type, which is optionally indented. The indentation signifies how deep in the MIME hierarchy the content-type is. """ def getIndent(line): """Get the current line's indentation, using four-space indents.""" count = 0 for char in line: if char != ' ': break count += 1 return count // 4 # A note on message/rfc822: The content of such an attachment is an # email with at least one header line. RFC2046 tells us: """ A # media type of "message/rfc822" indicates that the body contains an # encapsulated message, with the syntax of an RFC 822 message. # However, unlike top-level RFC 822 messages, the restriction that # each "message/rfc822" body must include a "From", "Date", and at # least one destination header is removed and replaced with the # requirement that at least one of "From", "Subject", or "Date" must # be present.""" # This means we have to add a newline after the mime-header before # the subject, otherwise the subject is part of the mime header not # part of the email header. table = { 'multipart/signed': ' boundary="boundary-%(indent)s";\n', 'multipart/mixed': ' boundary="boundary-%(indent)s";\n', 'multipart/alternative': ' boundary="boundary-%(indent)s";\n', 'text/plain': ' name="foo.txt"\nfoo\n', 'text/html': ' name="bar.html"\n<html><body>bar ></body></html>\n', 'application/pgp-signature': ' name="foo.gpg"\nfoo\n', 'application/pdf': ' name="foo.pdf"\nfoo\n', 'message/rfc822': '\nSubject: foo\n\nfoo\n' } parts = [] for line in spec.splitlines(): content_type = line.strip() if not content_type: continue indent = getIndent(line) if indent: parts.append('\n--boundary-%s\n' % indent) parts.append('Content-type: %s;\n' % content_type) parts.append(table[content_type] % {'indent': indent + 1}) for i in range(indent, 0, -1): parts.append('\n--boundary-%s--\n' % i) return email.message_from_file(StringIO(''.join(parts)), RoundupMessage)
def run_cgi(self): """ Execute the CGI command. Wrap an innner call in an error handler so all errors can be caught. """ try: self.inner_run_cgi() except client.NotFound: self.send_error(404, self.path) except client.Unauthorised as message: self.send_error(403, '%s (%s)' % (self.path, message)) except: exc, val, tb = sys.exc_info() if hasattr(socket, 'timeout') and isinstance(val, socket.timeout): self.log_error('timeout') else: # it'd be nice to be able to detect if these are going to have # any effect... self.send_response(400) self.send_header('Content-Type', 'text/html') self.end_headers() if self.DEBUG_MODE: try: reload(cgitb) self.wfile.write(s2b(cgitb.breaker())) self.wfile.write(s2b(cgitb.html())) except: s = StringIO() traceback.print_exc(None, s) self.wfile.write(b"<pre>") self.wfile.write(s2b(cgi.escape(s.getvalue()))) self.wfile.write(b"</pre>\n") else: # user feedback self.wfile.write(s2b(cgitb.breaker())) ts = time.ctime() self.wfile.write( s2b('''<p>%s: An error occurred. Please check the server log for more information.</p>''' % ts)) # out to the logfile print('EXCEPTION AT', ts) traceback.print_exc()
class MultipartTestCase(unittest.TestCase): def setUp(self): self.fp = StringIO() w = self.fp.write w('Content-Type: multipart/mixed; boundary="foo"\r\n\r\n') w('This is a multipart message. Ignore this bit.\r\n') w('\r\n--foo\r\n') w('Content-Type: text/plain\r\n\r\n') w('Hello, world!\r\n') w('\r\n') w('Blah blah\r\n') w('foo\r\n') w('-foo\r\n') w('\r\n--foo\r\n') w('Content-Type: multipart/alternative; boundary="bar"\r\n\r\n') w('This is a multipart message. Ignore this bit.\r\n') w('\r\n--bar\r\n') w('Content-Type: text/plain\r\n\r\n') w('Hello, world!\r\n') w('\r\n') w('Blah blah\r\n') w('\r\n--bar\r\n') w('Content-Type: text/html\r\n\r\n') w('<b>Hello, world!</b>\r\n') w('\r\n--bar--\r\n') w('\r\n--foo\r\n') w('Content-Type: text/plain\r\n\r\n') w('Last bit\n') w('\r\n--foo--\r\n') self.fp.seek(0) def testMultipart(self): m = email.message_from_file(self.fp, RoundupMessage) self.assert_(m is not None) it = iter(m.get_payload()) # first text/plain p = next(it, None) self.assert_(p is not None) self.assertEqual(p.get_content_type(), 'text/plain') self.assertEqual(p.get_payload(), 'Hello, world!\r\n\r\nBlah blah\r\nfoo\r\n-foo\r\n') # sub-multipart p = next(it, None) self.assert_(p is not None) self.assertEqual(p.get_content_type(), 'multipart/alternative') # sub-multipart text/plain qit = iter(p.get_payload()) q = next(qit, None) self.assert_(q is not None) self.assertEqual(q.get_content_type(), 'text/plain') self.assertEqual(q.get_payload(), 'Hello, world!\r\n\r\nBlah blah\r\n') # sub-multipart text/html q = next(qit, None) self.assert_(q is not None) self.assertEqual(q.get_content_type(), 'text/html') self.assertEqual(q.get_payload(), '<b>Hello, world!</b>\r\n') # sub-multipart end q = next(qit, None) self.assert_(q is None) # final text/plain p = next(it, None) self.assert_(p is not None) self.assertEqual(p.get_content_type(), 'text/plain') self.assertEqual(p.get_payload(), 'Last bit\n') # end p = next(it, None) self.assert_(p is None) def TestExtraction(self, spec, expected, convert_html_with=False): if convert_html_with: from roundup.dehtml import dehtml html2text = dehtml(convert_html_with).html2text else: html2text = None self.assertEqual( gen_message(spec).extract_content(html2text=html2text), expected) def testTextPlain(self): self.TestExtraction('text/plain', ('foo\n', [], False)) def testAttachedTextPlain(self): self.TestExtraction( """ multipart/mixed text/plain text/plain""", ('foo\n', [('foo.txt', 'text/plain', 'foo\n')], False)) def testMultipartMixed(self): self.TestExtraction( """ multipart/mixed text/plain application/pdf""", ('foo\n', [('foo.pdf', 'application/pdf', b'foo\n')], False)) def testMultipartMixedHtml(self): # test with html conversion enabled self.TestExtraction(""" multipart/mixed text/html application/pdf""", ('bar >\n', [ ('bar.html', 'text/html', '<html><body>bar ></body></html>\n'), ('foo.pdf', 'application/pdf', b'foo\n') ], False), convert_html_with='dehtml') # test with html conversion disabled self.TestExtraction(""" multipart/mixed text/html application/pdf""", (None, [ ('bar.html', 'text/html', '<html><body>bar ></body></html>\n'), ('foo.pdf', 'application/pdf', b'foo\n') ], False), convert_html_with=False) def testMultipartAlternative(self): self.TestExtraction( """ multipart/alternative text/plain application/pdf """, ('foo\n', [('foo.pdf', 'application/pdf', b'foo\n')], False)) def testMultipartAlternativeHtml(self): self.TestExtraction(""" multipart/alternative text/html application/pdf""", ('bar >\n', [ ('bar.html', 'text/html', '<html><body>bar ></body></html>\n'), ('foo.pdf', 'application/pdf', b'foo\n') ], False), convert_html_with='dehtml') self.TestExtraction(""" multipart/alternative text/html application/pdf""", (None, [ ('bar.html', 'text/html', '<html><body>bar ></body></html>\n'), ('foo.pdf', 'application/pdf', b'foo\n') ], False), convert_html_with=False) def testMultipartAlternativeHtmlText(self): # text should take priority over html when html is first self.TestExtraction(""" multipart/alternative text/html text/plain application/pdf""", ('foo\n', [ ('bar.html', 'text/html', '<html><body>bar ></body></html>\n'), ('foo.pdf', 'application/pdf', b'foo\n') ], False), convert_html_with='dehtml') # text should take priority over html when text is first self.TestExtraction(""" multipart/alternative text/plain text/html application/pdf""", ('foo\n', [ ('bar.html', 'text/html', '<html><body>bar ></body></html>\n'), ('foo.pdf', 'application/pdf', b'foo\n') ], False), convert_html_with='dehtml') # text should take priority over html when text is second and # html is disabled self.TestExtraction(""" multipart/alternative text/html text/plain application/pdf""", ('foo\n', [ ('bar.html', 'text/html', '<html><body>bar ></body></html>\n'), ('foo.pdf', 'application/pdf', b'foo\n') ], False), convert_html_with=False) # text should take priority over html when text is first and # html is disabled self.TestExtraction(""" multipart/alternative text/plain text/html application/pdf""", ('foo\n', [ ('bar.html', 'text/html', '<html><body>bar ></body></html>\n'), ('foo.pdf', 'application/pdf', b'foo\n') ], False), convert_html_with=False) def testDeepMultipartAlternative(self): self.TestExtraction( """ multipart/mixed multipart/alternative text/plain application/pdf """, ('foo\n', [('foo.pdf', 'application/pdf', b'foo\n')], False)) def testSignedText(self): self.TestExtraction( """ multipart/signed text/plain application/pgp-signature""", ('foo\n', [], False)) def testSignedAttachments(self): self.TestExtraction( """ multipart/signed multipart/mixed text/plain application/pdf application/pgp-signature""", ('foo\n', [('foo.pdf', 'application/pdf', b'foo\n')], False)) def testAttachedSignature(self): self.TestExtraction( """ multipart/mixed text/plain application/pgp-signature""", ('foo\n', [ ('foo.gpg', 'application/pgp-signature', b'foo\n') ], False)) def testMessageRfc822(self): self.TestExtraction(""" multipart/mixed message/rfc822""", (None, [ ('foo.eml', 'message/rfc822', 'Subject: foo\n\nfoo\n') ], False))
def StringIO(self): # Third-party products wishing to provide a full Unicode-aware # StringIO can do so by monkey-patching this method. return StringIO()