def test_UID(self): self.assertEqual(parse_fetch_response([b'23 (UID 76)']), {76: { b'SEQ': 23 }}) self.assertEqual(parse_fetch_response([b'23 (uiD 76)']), {76: { b'SEQ': 23 }})
def test_repeated_UID(self): self.assertEquals(parse_fetch_response(['23 (UID 76 FOO 123 UID 76 GOO 321)']), {76: {'FOO': 123, 'GOO': 321, 'SEQ': 23}}) self.assertEquals(parse_fetch_response(['23 (UID 76 FOO 123', 'UID 76 GOO 321)']), {76: {'FOO': 123, 'GOO': 321, 'SEQ': 23}})
def test_literals(self): self.assertEqual( parse_fetch_response( [(b"1 (RFC822.TEXT {4}", b"body"), (b" RFC822 {21}", b"Subject: test\r\n\r\nbody"), b")"] ), {1: {b"RFC822.TEXT": b"body", b"RFC822": b"Subject: test\r\n\r\nbody", b"SEQ": 1}}, )
def test_same_message_appearing_multiple_times(self): # This can occur when server sends unsolicited FETCH responses # (e.g. RFC 4551) self.assertEqual( parse_fetch_response([b"2 (FLAGS (Foo Bar)) ", b"2 (MODSEQ 4)"]), {2: {b"FLAGS": (b"Foo", b"Bar"), b"SEQ": 2, b"MODSEQ": 4}}, )
def test_INTERNALDATE_normalised(self): output = parse_fetch_response([b'3 (INTERNALDATE " 9-Feb-2007 17:08:08 -0430")']) dt = output[3][b'INTERNALDATE'] self.assertTrue(dt.tzinfo is None) # Returned date should be in local timezone expected_dt = datetime_to_native( datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(-4 * 60 - 30))) self.assertEqual(dt, expected_dt)
def test_ENVELOPE(self): envelope_str = ('1 (ENVELOPE ( ' '"Sun, 24 Mar 2013 22:06:10 +0200" ' '"subject" ' '(("name" NIL "address1" "domain1.com")) ' '((NIL NIL "address2" "domain2.com")) ' '(("name" NIL "address3" "domain3.com")) ' 'NIL' '((NIL NIL "address4" "domain4.com") ' '("person" NIL "address4b" "domain4b.com")) ' 'NIL "<reply-to-id>" "<msg_id>"))') output = parse_fetch_response([envelope_str], normalise_times=False) self.assertSequenceEqual( output[1]['ENVELOPE'], Envelope(datetime(2013, 3, 24, 22, 6, 10, tzinfo=FixedOffset(120)), "subject", (Address("name", None, "address1", "domain1.com"), ), (Address(None, None, "address2", "domain2.com"), ), (Address("name", None, "address3", "domain3.com"), ), None, (Address(None, None, "address4", "domain4.com"), Address("person", None, "address4b", "domain4b.com")), None, "<reply-to-id>", "<msg_id>"))
def test_literals(self): self.assertEqual(parse_fetch_response([(b'1 (RFC822.TEXT {4}', b'body'), (b' RFC822 {21}', b'Subject: test\r\n\r\nbody'), b')']), {1: {b'RFC822.TEXT': b'body', b'RFC822': b'Subject: test\r\n\r\nbody', b'SEQ': 1}})
def test_ENVELOPE_with_empty_addresses(self): envelope_str = ( b'1 (ENVELOPE ( ' b'NIL ' b'"subject" ' b'(("name" NIL "address1" "domain1.com") NIL) ' b'(NIL (NIL NIL "address2" "domain2.com")) ' b'(("name" NIL "address3" "domain3.com") NIL ("name" NIL "address3b" "domain3b.com")) ' b'NIL' b'((NIL NIL "address4" "domain4.com") ' b'("person" NIL "address4b" "domain4b.com")) ' b'NIL "<reply-to-id>" "<msg_id>"))') output = parse_fetch_response([envelope_str], normalise_times=False) self.assertSequenceEqual( output[1][b'ENVELOPE'], Envelope(None, b"subject", (Address(b"name", None, b"address1", b"domain1.com"), ), (Address(None, None, b"address2", b"domain2.com"), ), (Address(b"name", None, b"address3", b"domain3.com"), Address(b"name", None, b"address3b", b"domain3b.com")), None, (Address(None, None, b"address4", b"domain4.com"), Address(b"person", None, b"address4b", b"domain4b.com")), None, b"<reply-to-id>", b"<msg_id>"))
def test_simple_pairs(self): self.assertEqual(parse_fetch_response([b'23 (ABC 123 StUfF "hello")']), {23: { b'ABC': 123, b'STUFF': b'hello', b'SEQ': 23 }})
def check_BODYish_nested_multipart(self, respType): text = b'1 (' + respType + b'(' \ b'(' \ b'("text" "html" ("charset" "utf-8") NIL NIL "7bit" 97 3 NIL NIL NIL NIL)' \ b'("text" "plain" ("charset" "utf-8") NIL NIL "7bit" 62 3 NIL NIL NIL NIL)' \ b'"alternative" ("boundary" "===============8211050864078048428==") NIL NIL NIL' \ b')' \ b'("text" "plain" ("charset" "utf-8") NIL NIL "7bit" 16 1 NIL ("attachment" ("filename" "attachment.txt")) NIL NIL) ' \ b'"mixed" ("boundary" "===============0373402508605428821==") NIL NIL NIL))' parsed = parse_fetch_response([text]) self.assertEqual(parsed, {1: { respType: ( [ ( [ (b'text', b'html', (b'charset', b'utf-8'), None, None, b'7bit', 97, 3, None, None, None, None), (b'text', b'plain', (b'charset', b'utf-8'), None, None, b'7bit', 62, 3, None, None, None, None) ], b'alternative', (b'boundary', b'===============8211050864078048428=='), None, None, None ), (b'text', b'plain', (b'charset', b'utf-8'), None, None, b'7bit', 16, 1, None, (b'attachment', (b'filename', b'attachment.txt')), None, None) ], b'mixed', (b'boundary', b'===============0373402508605428821=='), None, None, None, ), b'SEQ': 1, }}) self.assertTrue(parsed[1][respType].is_multipart) self.assertTrue(parsed[1][respType][0][0].is_multipart) self.assertFalse(parsed[1][respType][0][0][0][0].is_multipart)
def test_ENVELOPE_with_no_date(self): envelope_str = ( b'1 (ENVELOPE ( ' b'NIL ' b'"subject" ' b'NIL ' b'NIL ' b'NIL ' b'NIL ' b'NIL ' b'NIL ' b'"<reply-to-id>" ' b'"<msg_id>"))' ) output = parse_fetch_response([envelope_str], normalise_times=False) self.assertSequenceEqual(output[1][b'ENVELOPE'], Envelope( None, b"subject", None, None, None, None, None, None, b"<reply-to-id>", b"<msg_id>" ) )
def test_INTERNALDATE(self): out = parse_fetch_response( [b'1 (INTERNALDATE " 9-Feb-2007 17:08:08 -0430")'], normalise_times=False) self.assertEqual( out[1][b'INTERNALDATE'], datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(-4 * 60 - 30)))
def check_BODYish_single_part(self, respType): text = '123 (UID 317 %s ("TEXT" "PLAIN" ("CHARSET" "us-ascii") NIL NIL "7BIT" 16 1))' % respType parsed = parse_fetch_response([text]) self.assertEquals(parsed, {317: {respType: ('TEXT', 'PLAIN', ('CHARSET', 'us-ascii'), None, None, '7BIT', 16, 1), 'SEQ': 123 } }) self.assertFalse(parsed[317][respType].is_multipart)
def test_not_uid_is_key(self): self.assertEqual( parse_fetch_response([b'23 (UID 76)'], uid_is_key=False), {23: { b'UID': 76, b'SEQ': 23 }})
def test_ENVELOPE(self): envelope_str = (b'1 (ENVELOPE ( ' b'"Sun, 24 Mar 2013 22:06:10 +0200" ' b'"subject" ' b'(("name" NIL "address1" "domain1.com")) ' # from (name and address) b'((NIL NIL "address2" "domain2.com")) ' # sender (just address) b'(("name" NIL "address3" "domain3.com") NIL) ' # reply to b'NIL' # to (no address) b'((NIL NIL "address4" "domain4.com") ' # cc b'("person" NIL "address4b" "domain4b.com")) ' b'NIL ' # bcc b'"<reply-to-id>" ' b'"<msg_id>"))') output = parse_fetch_response([envelope_str], normalise_times=False) self.assertSequenceEqual(output[1][b'ENVELOPE'], Envelope( datetime(2013, 3, 24, 22, 6, 10, tzinfo=FixedOffset(120)), b"subject", (Address(b"name", None, b"address1", b"domain1.com"),), (Address(None, None, b"address2", b"domain2.com"),), (Address(b"name", None, b"address3", b"domain3.com"),), None, (Address(None, None, b"address4", b"domain4.com"), Address(b"person", None, b"address4b", b"domain4b.com")), None, b"<reply-to-id>", b"<msg_id>" ) )
def test_same_message_appearing_multiple_times(self): # This can occur when server sends unsolicited FETCH responses # (e.g. RFC 4551) self.assertEqual(parse_fetch_response( [b"2 (FLAGS (Foo Bar)) ", b"2 (MODSEQ 4)"]), {2: {b'FLAGS': (b'Foo', b'Bar'), b'SEQ': 2, b'MODSEQ': 4}})
def test_literals(self): self.assertEquals(parse_fetch_response([('1 (RFC822.TEXT {4}', 'body'), (' RFC822 {21}', 'Subject: test\r\n\r\nbody'), ')']), {1: {'RFC822.TEXT': 'body', 'RFC822': 'Subject: test\r\n\r\nbody', 'SEQ': 1}})
def test_ENVELOPE_with_empty_addresses(self): envelope_str = (b'1 (ENVELOPE ( ' b'NIL ' b'"subject" ' b'(("name" NIL "address1" "domain1.com") NIL) ' b'(NIL (NIL NIL "address2" "domain2.com")) ' b'(("name" NIL "address3" "domain3.com") NIL ("name" NIL "address3b" "domain3b.com")) ' b'NIL' b'((NIL NIL "address4" "domain4.com") ' b'("person" NIL "address4b" "domain4b.com")) ' b'NIL "<reply-to-id>" "<msg_id>"))') output = parse_fetch_response([envelope_str], normalise_times=False) self.assertSequenceEqual(output[1][b'ENVELOPE'], Envelope( None, b"subject", (Address(b"name", None, b"address1", b"domain1.com"),), (Address(None, None, b"address2", b"domain2.com"),), (Address(b"name", None, b"address3", b"domain3.com"), Address(b"name", None, b"address3b", b"domain3b.com")), None, (Address(None, None, b"address4", b"domain4.com"), Address(b"person", None, b"address4b", b"domain4b.com")), None, b"<reply-to-id>", b"<msg_id>" ) )
def test_ENVELOPE(self): envelope_str = ( b'1 (ENVELOPE ( ' b'"Sun, 24 Mar 2013 22:06:10 +0200" ' b'"subject" ' b'(("name" NIL "address1" "domain1.com")) ' # from (name and address) b'((NIL NIL "address2" "domain2.com")) ' # sender (just address) b'(("name" NIL "address3" "domain3.com") NIL) ' # reply to b'NIL' # to (no address) b'((NIL NIL "address4" "domain4.com") ' # cc b'("person" NIL "address4b" "domain4b.com")) ' b'NIL ' # bcc b'"<reply-to-id>" ' b'"<msg_id>"))') output = parse_fetch_response([envelope_str], normalise_times=False) self.assertSequenceEqual( output[1][b'ENVELOPE'], Envelope(datetime(2013, 3, 24, 22, 6, 10, tzinfo=FixedOffset(120)), b"subject", (Address(b"name", None, b"address1", b"domain1.com"), ), (Address(None, None, b"address2", b"domain2.com"), ), (Address(b"name", None, b"address3", b"domain3.com"), ), None, (Address(None, None, b"address4", b"domain4.com"), Address(b"person", None, b"address4b", b"domain4b.com")), None, b"<reply-to-id>", b"<msg_id>"))
def check(date_str, expected_dt): output = parse_fetch_response(['3 (INTERNALDATE "%s")' % date_str]) actual_dt = output[3]['INTERNALDATE'] self.assertTrue(actual_dt.tzinfo is None) # Returned date should be in local timezone expected_dt = datetime_to_native(expected_dt) self.assertEqual(actual_dt, expected_dt)
def check(date_str, expected_dt): output = parse_fetch_response(['3 (INTERNALDATE "%s")' % date_str]) self.assertEquals(output.keys(), [3]) self.assertEquals(set(output[3].keys()), set(['INTERNALDATE', 'SEQ'])) actual_dt = output[3]['INTERNALDATE'] self.assert_(actual_dt.tzinfo is None) # Returned date should be in local timezone expected_dt = datetime_to_native(expected_dt) self.assert_(actual_dt == expected_dt, '%s != %s' % (actual_dt, expected_dt))
def test_multiple_messages(self): self.assertEqual(parse_fetch_response( [b"2 (FLAGS (Foo Bar)) ", b"7 (FLAGS (Baz Sneeve))"]), { 2: {b'FLAGS': (b'Foo', b'Bar'), b'SEQ': 2}, 7: {b'FLAGS': (b'Baz', b'Sneeve'), b'SEQ': 7}, })
def test_INTERNALDATE_NIL(self): out = parse_fetch_response( [b'1 (INTERNALDATE NIL)'] ) self.assertEqual( out[1][b'INTERNALDATE'], None )
def test_partial_fetch(self): body = '01234567890123456789' self.assertEqual( parse_fetch_response([('123 (UID 367 BODY[]<0> {20}', body), ')']), {367: { 'BODY[]<0>': body, 'SEQ': 123 }})
def test_multiple_messages(self): self.assertEquals(parse_fetch_response( ["2 (FLAGS (Foo Bar)) ", "7 (FLAGS (Baz Sneeve))"]), { 2: {'FLAGS': ('Foo', 'Bar'), 'SEQ': 2}, 7: {'FLAGS': ('Baz', 'Sneeve'), 'SEQ': 7}, })
def check_BODYish_single_part(self, respType): text = b"123 (UID 317 " + respType + b'("TEXT" "PLAIN" ("CHARSET" "us-ascii") NIL NIL "7BIT" 16 1))' parsed = parse_fetch_response([text]) self.assertEqual( parsed, {317: {respType: (b"TEXT", b"PLAIN", (b"CHARSET", b"us-ascii"), None, None, b"7BIT", 16, 1), b"SEQ": 123}}, ) self.assertFalse(parsed[317][respType].is_multipart)
def test_literals_and_keys_with_square_brackets(self): self.assertEqual( parse_fetch_response([(b'1 (BODY[TEXT] {11}', b'Hi there.\r\n'), b')']), {1: { b'BODY[TEXT]': b'Hi there.\r\n', b'SEQ': 1 }})
def test_INTERNALDATE(self): out = parse_fetch_response( [b'1 (INTERNALDATE " 9-Feb-2007 17:08:08 -0430")'], normalise_times=False ) self.assertEqual( out[1][b'INTERNALDATE'], datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(-4 * 60 - 30)) )
def test_mixed_types(self): self.assertEquals(parse_fetch_response([('1 (INTERNALDATE " 9-Feb-2007 17:08:08 +0100" RFC822 {21}', 'Subject: test\r\n\r\nbody'), ')']), {1: {'INTERNALDATE': datetime_to_native(datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(60))), 'RFC822': 'Subject: test\r\n\r\nbody', 'SEQ': 1}})
def check_BODYish_multipart(self, respType): text = '123 (UID 269 %s (("TEXT" "HTML" ("CHARSET" "us-ascii") NIL NIL "QUOTED-PRINTABLE" 55 3)' \ '("TEXT" "PLAIN" ("CHARSET" "us-ascii") NIL NIL "7BIT" 26 1) "MIXED"))' \ % respType parsed = parse_fetch_response([text]) self.assertEquals(parsed, {269: {respType: ([('TEXT', 'HTML', ('CHARSET', 'us-ascii'), None, None, 'QUOTED-PRINTABLE', 55, 3), ('TEXT', 'PLAIN', ('CHARSET', 'us-ascii'), None, None, '7BIT', 26, 1)], 'MIXED'), 'SEQ': 123} }) self.assertTrue(parsed[269][respType].is_multipart)
def check_BODYish_single_part(self, respType): text = b'123 (UID 317 ' + respType + \ b'("TEXT" "PLAIN" ("CHARSET" "us-ascii") NIL NIL "7BIT" 16 1))' parsed = parse_fetch_response([text]) self.assertEqual(parsed, { 317: { respType: (b'TEXT', b'PLAIN', (b'CHARSET', b'us-ascii'), None, None, b'7BIT', 16, 1), b'SEQ': 123 } }) self.assertFalse(parsed[317][respType].is_multipart)
def test_mixed_types(self): self.assertEqual(parse_fetch_response([( b'1 (INTERNALDATE " 9-Feb-2007 17:08:08 +0100" RFC822 {21}', b'Subject: test\r\n\r\nbody' ), b')']), { 1: { b'INTERNALDATE': datetime_to_native(datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(60))), b'RFC822': b'Subject: test\r\n\r\nbody', b'SEQ': 1 } })
def test_BODY_HEADER_FIELDS(self): header_text = b'Subject: A subject\r\nFrom: Some one <*****@*****.**>\r\n\r\n' self.assertEqual( parse_fetch_response([ (b'123 (UID 31710 BODY[HEADER.FIELDS (from subject)] {57}', header_text), b')' ]), { 31710: { b'BODY[HEADER.FIELDS (FROM SUBJECT)]': header_text, b'SEQ': 123 } })
def read_envelope(self, env, normalise_times=True): tag = b'001' msg_id = b'1' self.tagged_commands[tag] = None self.file = self.readfile = BytesIO(b'* ' + msg_id + b' FETCH ( ENVELOPE (' + env + b'))\r\n' + tag + b' OK FETCH completed\r\n') typ, data = self._command_complete('FETCH', tag) typ, data = self._untagged_response(typ, data, 'FETCH') resp = parse_fetch_response(data, normalise_times, True) return resp[int(msg_id)][b'ENVELOPE']
def check_BODYish_multipart(self, respType): text = b'123 (UID 269 ' + respType + b' ' \ b'(("TEXT" "HTML" ("CHARSET" "us-ascii") NIL NIL "QUOTED-PRINTABLE" 55 3)' \ b'("TEXT" "PLAIN" ("CHARSET" "us-ascii") NIL NIL "7BIT" 26 1) "MIXED"))' parsed = parse_fetch_response([text]) self.assertEqual(parsed, { 269: { respType: ([(b'TEXT', b'HTML', (b'CHARSET', b'us-ascii'), None, None, b'QUOTED-PRINTABLE', 55, 3), (b'TEXT', b'PLAIN', (b'CHARSET', b'us-ascii'), None, None, b'7BIT', 26, 1)], b'MIXED'), b'SEQ': 123} }) self.assertTrue(parsed[269][respType].is_multipart)
def test_mixed_types(self): self.assertEqual( parse_fetch_response( [(b'1 (INTERNALDATE " 9-Feb-2007 17:08:08 +0100" RFC822 {21}', b"Subject: test\r\n\r\nbody"), b")"] ), { 1: { b"INTERNALDATE": datetime_to_native(datetime(2007, 2, 9, 17, 8, 8, 0, FixedOffset(60))), b"RFC822": b"Subject: test\r\n\r\nbody", b"SEQ": 1, } }, )
def check_BODYish_multipart(self, respType): text = ( b"123 (UID 269 " + respType + b" " b'(("TEXT" "HTML" ("CHARSET" "us-ascii") NIL NIL "QUOTED-PRINTABLE" 55 3)' b'("TEXT" "PLAIN" ("CHARSET" "us-ascii") NIL NIL "7BIT" 26 1) "MIXED"))' ) parsed = parse_fetch_response([text]) self.assertEqual( parsed, { 269: { respType: ( [ (b"TEXT", b"HTML", (b"CHARSET", b"us-ascii"), None, None, b"QUOTED-PRINTABLE", 55, 3), (b"TEXT", b"PLAIN", (b"CHARSET", b"us-ascii"), None, None, b"7BIT", 26, 1), ], b"MIXED", ), b"SEQ": 123, } }, ) self.assertTrue(parsed[269][respType].is_multipart)
def test_basic(self): self.assertEqual(parse_fetch_response([b'4 ()']), {4: {b'SEQ': 4}})
def check(date_str, expected_dt): output = parse_fetch_response(['3 (INTERNALDATE "%s")' % date_str], normalise_times=False) actual_dt = output[3]['INTERNALDATE'] self.assertEqual(actual_dt, expected_dt)
def test_none_special_case(self): self.assertEqual(parse_fetch_response([None]), {})
def test_FLAGS(self): self.assertEqual(parse_fetch_response([b'23 (FLAGS (\Seen Stuff))']), {23: { b'SEQ': 23, b'FLAGS': (br'\Seen', b'Stuff') }})
def test_partial_fetch(self): body = b'01234567890123456789' self.assertEqual(parse_fetch_response( [(b'123 (UID 367 BODY[]<0> {20}', body), b')']), {367: {b'BODY[]<0>': body, b'SEQ': 123}})
def test_simple_pairs(self): self.assertEqual(parse_fetch_response([b'23 (ABC 123 StUfF "hello")']), {23: {b'ABC': 123, b'STUFF': b'hello', b'SEQ': 23}})
def test_BODY_HEADER_FIELDS(self): header_text = b'Subject: A subject\r\nFrom: Some one <*****@*****.**>\r\n\r\n' self.assertEqual(parse_fetch_response( [(b'123 (UID 31710 BODY[HEADER.FIELDS (from subject)] {57}', header_text), b')']), {31710: {b'BODY[HEADER.FIELDS (FROM SUBJECT)]': header_text, b'SEQ': 123}})
def test_literals_and_keys_with_square_brackets(self): self.assertEqual(parse_fetch_response([(b'1 (BODY[TEXT] {11}', b'Hi there.\r\n'), b')']), {1: {b'BODY[TEXT]': b'Hi there.\r\n', b'SEQ': 1}})
def test_UID(self): self.assertEqual(parse_fetch_response([b'23 (UID 76)']), {76: {b'SEQ': 23}}) self.assertEqual(parse_fetch_response([b'23 (uiD 76)']), {76: {b'SEQ': 23}})
def test_not_uid_is_key(self): self.assertEqual(parse_fetch_response([b'23 (UID 76)'], uid_is_key=False), {23: {b'UID': 76, b'SEQ': 23}})
def test_FLAGS(self): self.assertEqual(parse_fetch_response([b'23 (FLAGS (\Seen Stuff))']), {23: {b'SEQ': 23, b'FLAGS': (br'\Seen', b'Stuff')}})