python-3.6.zip added from Github

README.cosmo contains the necessary links.
This commit is contained in:
ahgamut 2021-08-08 09:38:33 +05:30 committed by Justine Tunney
parent 75fc601ff5
commit 0c4c56ff39
4219 changed files with 1968626 additions and 0 deletions

View file

@ -0,0 +1,166 @@
import os
import unittest
import collections
import email
from email.message import Message
from email._policybase import compat32
from test.support import load_package_tests
from test.test_email import __file__ as landmark
# Load all tests in package
def load_tests(*args):
return load_package_tests(os.path.dirname(__file__), *args)
# helper code used by a number of test modules.
def openfile(filename, *args, **kws):
path = os.path.join(os.path.dirname(landmark), 'data', filename)
return open(path, *args, **kws)
# Base test class
class TestEmailBase(unittest.TestCase):
maxDiff = None
# Currently the default policy is compat32. By setting that as the default
# here we make minimal changes in the test_email tests compared to their
# pre-3.3 state.
policy = compat32
# Likewise, the default message object is Message.
message = Message
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.addTypeEqualityFunc(bytes, self.assertBytesEqual)
# Backward compatibility to minimize test_email test changes.
ndiffAssertEqual = unittest.TestCase.assertEqual
def _msgobj(self, filename):
with openfile(filename) as fp:
return email.message_from_file(fp, policy=self.policy)
def _str_msg(self, string, message=None, policy=None):
if policy is None:
policy = self.policy
if message is None:
message = self.message
return email.message_from_string(string, message, policy=policy)
def _bytes_msg(self, bytestring, message=None, policy=None):
if policy is None:
policy = self.policy
if message is None:
message = self.message
return email.message_from_bytes(bytestring, message, policy=policy)
def _make_message(self):
return self.message(policy=self.policy)
def _bytes_repr(self, b):
return [repr(x) for x in b.splitlines(keepends=True)]
def assertBytesEqual(self, first, second, msg):
"""Our byte strings are really encoded strings; improve diff output"""
self.assertEqual(self._bytes_repr(first), self._bytes_repr(second))
def assertDefectsEqual(self, actual, expected):
self.assertEqual(len(actual), len(expected), actual)
for i in range(len(actual)):
self.assertIsInstance(actual[i], expected[i],
'item {}'.format(i))
def parameterize(cls):
"""A test method parameterization class decorator.
Parameters are specified as the value of a class attribute that ends with
the string '_params'. Call the portion before '_params' the prefix. Then
a method to be parameterized must have the same prefix, the string
'_as_', and an arbitrary suffix.
The value of the _params attribute may be either a dictionary or a list.
The values in the dictionary and the elements of the list may either be
single values, or a list. If single values, they are turned into single
element tuples. However derived, the resulting sequence is passed via
*args to the parameterized test function.
In a _params dictionary, the keys become part of the name of the generated
tests. In a _params list, the values in the list are converted into a
string by joining the string values of the elements of the tuple by '_' and
converting any blanks into '_'s, and this become part of the name.
The full name of a generated test is a 'test_' prefix, the portion of the
test function name after the '_as_' separator, plus an '_', plus the name
derived as explained above.
For example, if we have:
count_params = range(2)
def count_as_foo_arg(self, foo):
self.assertEqual(foo+1, myfunc(foo))
we will get parameterized test methods named:
test_foo_arg_0
test_foo_arg_1
test_foo_arg_2
Or we could have:
example_params = {'foo': ('bar', 1), 'bing': ('bang', 2)}
def example_as_myfunc_input(self, name, count):
self.assertEqual(name+str(count), myfunc(name, count))
and get:
test_myfunc_input_foo
test_myfunc_input_bing
Note: if and only if the generated test name is a valid identifier can it
be used to select the test individually from the unittest command line.
The values in the params dict can be a single value, a tuple, or a
dict. If a single value of a tuple, it is passed to the test function
as positional arguments. If a dict, it is a passed via **kw.
"""
paramdicts = {}
testers = collections.defaultdict(list)
for name, attr in cls.__dict__.items():
if name.endswith('_params'):
if not hasattr(attr, 'keys'):
d = {}
for x in attr:
if not hasattr(x, '__iter__'):
x = (x,)
n = '_'.join(str(v) for v in x).replace(' ', '_')
d[n] = x
attr = d
paramdicts[name[:-7] + '_as_'] = attr
if '_as_' in name:
testers[name.split('_as_')[0] + '_as_'].append(name)
testfuncs = {}
for name in paramdicts:
if name not in testers:
raise ValueError("No tester found for {}".format(name))
for name in testers:
if name not in paramdicts:
raise ValueError("No params found for {}".format(name))
for name, attr in cls.__dict__.items():
for paramsname, paramsdict in paramdicts.items():
if name.startswith(paramsname):
testnameroot = 'test_' + name[len(paramsname):]
for paramname, params in paramsdict.items():
if hasattr(params, 'keys'):
test = (lambda self, name=name, params=params:
getattr(self, name)(**params))
else:
test = (lambda self, name=name, params=params:
getattr(self, name)(*params))
testname = testnameroot + '_' + paramname
test.__name__ = testname
testfuncs[testname] = test
for key, value in testfuncs.items():
setattr(cls, key, value)
return cls

View file

@ -0,0 +1,4 @@
from test.test_email import load_tests
import unittest
unittest.main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

View file

@ -0,0 +1,19 @@
Return-Path: <bbb@zzz.org>
Delivered-To: bbb@zzz.org
Received: by mail.zzz.org (Postfix, from userid 889)
id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
From: bbb@ddd.com (John X. Doe)
To: bbb@zzz.org
Subject: This is a test message
Date: Fri, 4 May 2001 14:05:44 -0400
Hi,
Do you like this message?
-Me

View file

@ -0,0 +1,136 @@
MIME-version: 1.0
From: ppp-request@zzz.org
Sender: ppp-admin@zzz.org
To: ppp@zzz.org
Subject: Ppp digest, Vol 1 #2 - 5 msgs
Date: Fri, 20 Apr 2001 20:18:00 -0400 (EDT)
X-Mailer: Mailman v2.0.4
X-Mailman-Version: 2.0.4
Content-Type: multipart/mixed; boundary="192.168.1.2.889.32614.987812255.500.21814"
--192.168.1.2.889.32614.987812255.500.21814
Content-type: text/plain; charset=us-ascii
Content-description: Masthead (Ppp digest, Vol 1 #2)
Send Ppp mailing list submissions to
ppp@zzz.org
To subscribe or unsubscribe via the World Wide Web, visit
http://www.zzz.org/mailman/listinfo/ppp
or, via email, send a message with subject or body 'help' to
ppp-request@zzz.org
You can reach the person managing the list at
ppp-admin@zzz.org
When replying, please edit your Subject line so it is more specific
than "Re: Contents of Ppp digest..."
--192.168.1.2.889.32614.987812255.500.21814
Content-type: text/plain; charset=us-ascii
Content-description: Today's Topics (5 msgs)
Today's Topics:
1. testing #1 (Barry A. Warsaw)
2. testing #2 (Barry A. Warsaw)
3. testing #3 (Barry A. Warsaw)
4. testing #4 (Barry A. Warsaw)
5. testing #5 (Barry A. Warsaw)
--192.168.1.2.889.32614.987812255.500.21814
Content-Type: multipart/digest; boundary="__--__--"
--__--__--
Message: 1
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Date: Fri, 20 Apr 2001 20:16:13 -0400
To: ppp@zzz.org
From: barry@digicool.com (Barry A. Warsaw)
Subject: [Ppp] testing #1
Precedence: bulk
hello
--__--__--
Message: 2
Date: Fri, 20 Apr 2001 20:16:21 -0400
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
To: ppp@zzz.org
From: barry@digicool.com (Barry A. Warsaw)
Precedence: bulk
hello
--__--__--
Message: 3
Date: Fri, 20 Apr 2001 20:16:25 -0400
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
To: ppp@zzz.org
From: barry@digicool.com (Barry A. Warsaw)
Subject: [Ppp] testing #3
Precedence: bulk
hello
--__--__--
Message: 4
Date: Fri, 20 Apr 2001 20:16:28 -0400
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
To: ppp@zzz.org
From: barry@digicool.com (Barry A. Warsaw)
Subject: [Ppp] testing #4
Precedence: bulk
hello
--__--__--
Message: 5
Date: Fri, 20 Apr 2001 20:16:32 -0400
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
To: ppp@zzz.org
From: barry@digicool.com (Barry A. Warsaw)
Subject: [Ppp] testing #5
Precedence: bulk
hello
--__--__----
--192.168.1.2.889.32614.987812255.500.21814
Content-type: text/plain; charset=us-ascii
Content-description: Digest Footer
_______________________________________________
Ppp mailing list
Ppp@zzz.org
http://www.zzz.org/mailman/listinfo/ppp
--192.168.1.2.889.32614.987812255.500.21814--
End of Ppp Digest

View file

@ -0,0 +1,16 @@
Return-Path: <bbb@zzz.org>
Delivered-To: bbb@zzz.org
Received: by mail.zzz.org (Postfix, from userid 889)
id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
From: bbb@ddd.com (John X. Doe)
To: bbb@zzz.org
Subject: This is a test message
Date: Fri, 4 May 2001 14:05:44 -0400
Hi,
Do you like this message?
-Me

View file

@ -0,0 +1,37 @@
Return-Path: <barry@python.org>
Delivered-To: barry@python.org
Received: by mail.python.org (Postfix, from userid 889)
id C2BF0D37C6; Tue, 11 Sep 2001 00:05:05 -0400 (EDT)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="h90VIIIKmx"
Content-Transfer-Encoding: 7bit
Message-ID: <15261.36209.358846.118674@anthem.python.org>
From: barry@python.org (Barry A. Warsaw)
To: barry@python.org
Subject: a simple multipart
Date: Tue, 11 Sep 2001 00:05:05 -0400
X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid
X-Attribution: BAW
X-Oblique-Strategy: Make a door into a window
--h90VIIIKmx
Content-Type: text/plain
Content-Disposition: inline;
filename="msg.txt"
Content-Transfer-Encoding: 7bit
a simple kind of mirror
to reflect upon our own
--h90VIIIKmx
Content-Type: text/plain
Content-Disposition: inline;
filename="msg.txt"
Content-Transfer-Encoding: 7bit
a simple kind of mirror
to reflect upon our own
--h90VIIIKmx--

View file

@ -0,0 +1,28 @@
From: foo
Subject: bar
To: baz
MIME-Version: 1.0
Content-Type: multipart/report; report-type=delivery-status;
boundary="D1690A7AC1.996856090/mail.example.com"
Message-Id: <20010803162810.0CA8AA7ACC@mail.example.com>
This is a MIME-encapsulated message.
--D1690A7AC1.996856090/mail.example.com
Content-Type: text/plain
Yadda yadda yadda
--D1690A7AC1.996856090/mail.example.com
Yadda yadda yadda
--D1690A7AC1.996856090/mail.example.com
Content-Type: message/rfc822
From: nobody@python.org
Yadda yadda yadda
--D1690A7AC1.996856090/mail.example.com--

View file

@ -0,0 +1,33 @@
Return-Path: <barry@python.org>
Delivered-To: barry@python.org
MIME-Version: 1.0
Content-Type: message/rfc822
Content-Description: forwarded message
Content-Transfer-Encoding: 7bit
Message-ID: <15265.9482.641338.555352@python.org>
From: barry@python.org (Barry A. Warsaw)
Sender: barry@python.org
To: barry@python.org
Subject: forwarded message from Barry A. Warsaw
Date: Thu, 13 Sep 2001 17:28:42 -0400
X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid
X-Attribution: BAW
X-Oblique-Strategy: Be dirty
X-Url: http://barry.wooz.org
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Return-Path: <barry@python.org>
Delivered-To: barry@python.org
Message-ID: <15265.9468.713530.98441@python.org>
From: barry@python.org (Barry A. Warsaw)
Sender: barry@python.org
To: barry@python.org
Subject: testing
Date: Thu, 13 Sep 2001 17:28:28 -0400
X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid
X-Attribution: BAW
X-Oblique-Strategy: Spectrum analysis
X-Url: http://barry.wooz.org

View file

@ -0,0 +1,83 @@
MIME-Version: 1.0
From: Barry <barry@digicool.com>
To: Dingus Lovers <cravindogs@cravindogs.com>
Subject: Here is your dingus fish
Date: Fri, 20 Apr 2001 19:35:02 -0400
Content-Type: multipart/mixed; boundary="BOUNDARY"
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
Hi there,
This is the dingus fish.
--BOUNDARY
Content-Type: image/gif; name="dingusfish.gif"
Content-Transfer-Encoding: base64
content-disposition: attachment; filename="dingusfish.gif"
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
--BOUNDARY--

View file

@ -0,0 +1,24 @@
MIME-Version: 1.0
From: Barry Warsaw <barry@python.org>
To: Dingus Lovers <cravindogs@cravindogs.com>
Subject: Lyrics
Date: Fri, 20 Apr 2001 19:35:02 -0400
Content-Type: multipart/mixed; boundary="BOUNDARY"
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
--BOUNDARY
Content-Type: text/html; charset="iso-8859-1"
--BOUNDARY
Content-Type: text/plain; charset="iso-8859-2"
--BOUNDARY
Content-Type: text/plain; charset="koi8-r"
--BOUNDARY--

View file

@ -0,0 +1,24 @@
MIME-Version: 1.0
From: Barry Warsaw <barry@python.org>
To: Dingus Lovers <cravindogs@cravindogs.com>
Subject: Lyrics
Date: Fri, 20 Apr 2001 19:35:02 -0400
Content-Type: multipart/mixed; boundary="BOUNDARY"
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
--BOUNDARY
Content-Type: text/html; charset="iso-8859-1"
--BOUNDARY
Content-Type: text/plain
--BOUNDARY
Content-Type: text/plain; charset="koi8-r"
--BOUNDARY--

View file

@ -0,0 +1,39 @@
MIME-Version: 1.0
From: Barry Warsaw <barry@python.org>
To: Dingus Lovers <cravindogs@cravindogs.com>
Subject: Lyrics
Date: Fri, 20 Apr 2001 19:35:02 -0400
Content-Type: multipart/mixed; boundary="BOUNDARY"
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
This is a 7bit encoded message.
--BOUNDARY
Content-Type: text/html; charset="iso-8859-1"
Content-Transfer-Encoding: Quoted-Printable
=A1This is a Quoted Printable encoded message!
--BOUNDARY
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: Base64
VGhpcyBpcyBhIEJhc2U2NCBlbmNvZGVkIG1lc3NhZ2Uu
--BOUNDARY
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: Base64
VGhpcyBpcyBhIEJhc2U2NCBlbmNvZGVkIG1lc3NhZ2UuCg==
--BOUNDARY
Content-Type: text/plain; charset="iso-8859-1"
This has no Content-Transfer-Encoding: header.
--BOUNDARY--

View file

@ -0,0 +1,7 @@
Content-Type: message/rfc822
MIME-Version: 1.0
Subject: The enclosing message
Subject: An enclosed message
Here is the body of the message.

View file

@ -0,0 +1,36 @@
MIME-Version: 1.0
From: Barry Warsaw <barry@python.org>
To: Dingus Lovers <cravindogs@cravindogs.com>
Subject: Lyrics
Date: Fri, 20 Apr 2001 19:35:02 -0400
Content-Type: multipart/mixed; boundary="BOUNDARY"
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
--BOUNDARY
Content-Type: text/html; charset="iso-8859-1"
--BOUNDARY
Content-Type: multipart/mixed; boundary="ANOTHER"
--ANOTHER
Content-Type: text/plain; charset="iso-8859-2"
--ANOTHER
Content-Type: text/plain; charset="iso-8859-3"
--ANOTHER--
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
--BOUNDARY
Content-Type: text/plain; charset="koi8-r"
--BOUNDARY--

View file

@ -0,0 +1,38 @@
MIME-Version: 1.0
From: Barry Warsaw <barry@python.org>
To: Dingus Lovers <cravindogs@cravindogs.com>
Subject: Lyrics
Date: Fri, 20 Apr 2001 19:35:02 -0400
Content-Type: multipart/mixed; boundary="BOUNDARY"
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
--BOUNDARY
Content-Type: text/html; charset="iso-8859-1"
--BOUNDARY
Content-Type: multipart/mixed; boundary="ANOTHER"
--ANOTHER
Content-Type: text/plain; charset="iso-8859-2"
--ANOTHER
Content-Type: text/plain; charset="iso-8859-3"
--ANOTHER--
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
--BOUNDARY
Content-Type: text/plain; charset="koi8-r"
--BOUNDARY--

View file

@ -0,0 +1,94 @@
MIME-Version: 1.0
From: Barry <barry@digicool.com>
To: Dingus Lovers <cravindogs@cravindogs.com>
Subject: Here is your dingus fish
Date: Fri, 20 Apr 2001 19:35:02 -0400
Content-Type: multipart/mixed; boundary="OUTER"
--OUTER
Content-Type: text/plain; charset="us-ascii"
A text/plain part
--OUTER
Content-Type: multipart/mixed; boundary=BOUNDARY
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
Hi there,
This is the dingus fish.
--BOUNDARY
Content-Type: image/gif; name="dingusfish.gif"
Content-Transfer-Encoding: base64
content-disposition: attachment; filename="dingusfish.gif"
R0lGODdhAAEAAfAAAP///wAAACwAAAAAAAEAAQAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq2
7gvH8kzX9o3n+s73/g8MCofEovGITGICTKbyCV0FDNOo9SqpQqpOrJfXzTQj2vD3TGtqL+NtGQ2f
qTXmxzuOd7WXdcc9DyjU53ewFni4s0fGhdiYaEhGBelICTNoV1j5NUnFcrmUqemjNifJVWpaOqaI
oFq3SspZsSraE7sHq3jr1MZqWvi662vxV4tD+pvKW6aLDOCLyur8PDwbanyDeq0N3DctbQYeLDvR
RY6t95m6UB0d3mwIrV7e2VGNvjjffukeJp4w7F65KecGFsTHQGAygOrgrWs1jt28Rc88KESYcGLA
/obvTkH6p+CinWJiJmIMqXGQwH/y4qk0SYjgQTczT3ajKZGfuI0uJ4kkVI/DT5s3/ejkxI0aT4Y+
YTYgWbImUaXk9nlLmnSh1qJiJFl0OpUqRK4oOy7NyRQtHWofhoYVxkwWXKUSn0YsS+fUV6lhqfYb
6ayd3Z5qQdG1B7bvQzaJjwUV2lixMUZ7JVsOlfjWVr/3NB/uFvnySBN6Dcb6rGwaRM3wsormw5cC
M9NxWy/bWdufudCvy8bOAjXjVVwta/uO21sE5RHBCzNFXtgq9ORtH4eYjVP4Yryo026nvkFmCeyA
B29efV6ravCMK5JwWd5897Qrx7ll38o6iHDZ/rXPR//feevhF4l7wjUGX3xq1eeRfM4RSJGBIV1D
z1gKPkfWag3mVBVvva1RlX5bAJTPR/2YqNtw/FkIYYEi/pIZiAdpcxpoHtmnYYoZtvhUftzdx5ZX
JSKDW405zkGcZzzGZ6KEv4FI224oDmijlEf+xp6MJK5ojY/ASeVUR+wsKRuJ+XFZ5o7ZeEime8t1
ouUsU6YjF5ZtUihhkGfCdFQLWQFJ3UXxmElfhQnR+eCdcDbkFZp6vTRmj56ApCihn5QGpaToNZmR
n3NVSpZcQpZ2KEONusaiCsKAug0wkQbJSFO+PTSjneGxOuFjPlUk3ovWvdIerjUg9ZGIOtGq/qeX
eCYrrCX+1UPsgTKGGRSbzd5q156d/gpfbJxe66eD5iQKrXj7RGgruGxs62qebBHUKS32CKluCiqZ
qh+pmehmEb71noAUoe5e9Zm17S7773V10pjrtG4CmuurCV/n6zLK5turWNhqOvFXbjhZrMD0YhKe
wR0zOyuvsh6MWrGoIuzvyWu5y1WIFAqmJselypxXh6dKLNOKEB98L88bS2rkNqqlKzCNJp9c0G0j
Gzh0iRrCbHSXmPR643QS+4rWhgFmnSbSuXCjS0xAOWkU2UdLqyuUNfHSFdUouy3bm5i5GnDM3tG8
doJ4r5tqu3pPbRSVfvs8uJzeNXhp3n4j/tZ42SwH7eaWUUOjc3qFV9453UHTXZfcLH+OeNs5g36x
lBnHvTm7EbMbLeuaLncao8vWCXimfo1o+843Ak6y4ChNeGntvAYvfLK4ezmoyNIbNCLTCXO9ZV3A
E8/s88RczPzDwI4Ob7XZyl7+9Miban29h+tJZPrE21wgvBphDfrrfPdCTPKJD/y98L1rZwHcV6Jq
Zab0metpuNIX/qAFPoz171WUaUb4HAhBSzHuHfjzHb3kha/2Cctis/ORArVHNYfFyYRH2pYIRzic
isVOfPWD1b6mRTqpCRBozzof6UZVvFXRxWIr3GGrEviGYgyPMfahheiSaLs/9QeFu7oZ/ndSY8DD
ya9x+uPed+7mxN2IzIISBOMLFYWVqC3Pew1T2nFuuCiwZS5/v6II10i4t1OJcUH2U9zxKodHsGGv
Oa+zkvNUYUOa/TCCRutF9MzDwdlUMJADTCGSbDQ5OV4PTamDoPEi6Ecc/RF5RWwkcdSXvSOaDWSn
I9LlvubFTQpuc6JKXLcKeb+xdbKRBnwREemXyjg6ME65aJiOuBgrktzykfPLJBKR9ClMavJ62/Ff
BlNIyod9yX9wcSXexnXFpvkrbXk64xsx5Db7wXKP5fSgsvwIMM/9631VLBfkmtbHRXpqmtei52hG
pUwSlo+BASQoeILDOBgREECxBBh5/iYmNsQ9dIv5+OI++QkqdsJPc3uykz5fkM+OraeekcQF7X4n
B5S67za5U967PmooGQhUXfF7afXyCD7ONdRe17QogYjVx38uLwtrS6nhTnm15LQUnu9E2uK6CNI/
1HOABj0ESwOjut4FEpFQpdNAm4K2LHnDWHNcmKB2ioKBogysVZtMO2nSxUdZ8Yk2kJc7URioLVI0
YgmtIwZj4LoeKemgnOnbUdGnzZ4Oa6scqiolBGqS6RgWNLu0RMhcaE6rhhU4hiuqFXPAG8fGwTPW
FKeLMtdVmXLSs5YJGF/YeVm7rREMlY3UYE+yCxbaMXX8y15m5zVHq6GOKDMynzII/jdUHdyVqIy0
ifX2+r/EgtZcvRzSb72gU9ui87M2VecjKildW/aFqaYhKoryUjfB/g4qtyVuc60xFDGmCxwjW+qu
zjuwl2GkOWn66+3QiiEctvd04OVvcCVzjgT7lrkvjVGKKHmmlDUKowSeikb5kK/mJReuWOxONx+s
ULsl+Lqb0CVn0SrVyJ6wt4t6yTeSCafhPhAf0OXn6L60UMxiLolFAtmN35S2Ob1lZpQ1r/n0Qb5D
oQ1zJiRVDgF8N3Q8TYfbi3DyWCy3lT1nxyBs6FT3S2GOzWRlxwKvlRP0RPJA9SjxEy0UoEnkA+M4
cnzLMJrBGWLFEaaUb5lvpqbq/loOaU5+DFuHPxo82/OZuM8FXG3oVNZhtWpMpb/0Xu5m/LfLhHZQ
7yuVI0MqZ7NE43imC8jH3IwGZlbPm0xkJYs7+2U48hXTsFSMqgGDvai0kLxyynKNT/waj+q1c1tz
GjOpPBgdCSq3UKZxCSsqFIY+O6JbAWGWcV1pwqLyj5sGqCF1xb1F3varUWqrJv6cN3PrUXzijtfZ
FshpBL3Xwr4GIPvU2N8EjrJgS1zl21rbXQMXeXc5jjFyrhpCzijSv/RQtyPSzHCFMhlME95fHglt
pRsX+dfSQjUeHAlpWzJ5iOo79Ldnaxai6bXTcGO3fp07ri7HLEmXXPlYi8bv/qVxvNcdra6m7Rlb
6JBTb5fd66VhFRjGArh2n7R1rDW4P5NOT9K0I183T2scYkeZ3q/VFyLb09U9ajzXBS8Kgkhc4mBS
kYY9cy3Vy9lUnuNJH8HGIclUilwnBtjUOH0gteGOZ4c/XNrhXLSYDyxfnD8z1pDy7rYRvDolhnbe
UMzxCZUs40s6s7UIvBnLgc0+vKuOkIXeOrDymlp+Zxra4MZLBbVrqD/jTJ597pDmnw5c4+DbyB88
9Cg9DodYcSuMZT/114pptqc/EuTjRPvH/z5slzI3tluOEBBLqOXLOX+0I5929tO97wkvl/atCz+y
xJrdwteW2FNW/NSmBP+f/maYtVs/bYyBC7Ox3jsYZHL05CIrBa/nS+b3bHfiYm4Ueil1YZZSgAUI
fFZ1dxUmeA2oQRQ3RuGXNGLFV9/XbGFGPV6kfzk1TBBCd+izc7q1H+OHMJwmaBX2IQNYVAKHYepV
SSGCe6CnbYHHETKGNe43EDvFgZr0gB/nVHPHZ80VV1ojOiI3XDvYIkl4ayo4bxQIgrFXWTvBI0nH
VElWMuw2aLUWCRHHf8ymVCHjFlJnOSojfevCYyyyZDH0IcvHhrsnQ5O1OsWzONuVVKIxSxiFZ/tR
fKDAf6xFTnw4O9Qig2VCfW2hJQrmMOuHW0W3dLQmCMO2ccdUd/xyfflH/olTiHZVdGwb8nIwRzSE
J15jFlOJuBZBZ4CiyHyd2IFylFlB+HgHhYabhWOGwYO1ZH/Og1dtQlFMk352CGRSIFTapnWQEUtN
l4zv8S0aaCFDyGCBqDUxZYpxGHX01y/JuH1xhn7TOCnNCI4eKDs5WGX4R425F4vF1o3BJ4vO0otq
I3rimI7jJY1jISqnBxknCIvruF83mF5wN4X7qGLIhR8A2Vg0yFERSIXn9Vv3GHy3Vj/WIkKddlYi
yIMv2I/VMjTLpW7pt05SWIZR0RPyxpB4SIUM9lBPGBl0GC7oSEEwRYLe4pJpZY2P0zbI1n+Oc44w
qY3PUnmF0ixjVpDD/mJ9wpOBGTVgXlaCaZiPcIWK5NiKBIiPdGaQ0TWGvAiG7nMchdZb7Vgf8zNi
MuMyzRdy/lePe9iC4TRx7WhhOQI/QiSVNAmAa2lT/piFbuh7ofJoYSZzrSZ1bvmWw3eN2nKUPVky
uPN5/VRfohRd0VYZoqhKIlU6TXYhJxmPUIloAwc1bPmHEpaZYZORHNlXUJM07hATwHR8MJYqkwWR
WaIezFhxSFlc8/Fq82hEnpeRozg3ULhhr9lAGtVEkCg5ZNRuuVleBPaZadhG0ZgkyPmDOTOKzViM
YgOcpukKqQcbjAWS0IleQ2ROjdh6A+md1qWdBRSX7iSYgFRTtRmBpJioieXJiHfJiMGIR9fJOn8I
MSfXYhspn4ooSa2mSAj4n+8Bmg03fBJZoPOJgsVZRxu1oOMRPXYYjdqjihFaEoZpXBREanuJoRI6
cibFinq4ngUKh/wQd/H5ofYCZ0HJXR62opZFaAT0iFIZo4DIiUojkjeqKiuoZirKo5Y1a7AWckGa
BkuYoD5lpDK6eUs6CkDqpETwl1EqpfhJpVeKpVl6EgUAADs=
--BOUNDARY--
--OUTER--

View file

@ -0,0 +1,23 @@
Return-Path: <bbb@zzz.org>
Delivered-To: bbb@zzz.org
Received: by mail.zzz.org (Postfix, from userid 889)
id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
MIME-Version: 1.0
Content-Type: text; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
From: bbb@ddd.com (John X. Doe)
To: bbb@zzz.org
Subject: This is a test message
Date: Fri, 4 May 2001 14:05:44 -0400
Hi,
I'm sorry but I'm using a drainbread ISP, which although big and
wealthy can't seem to generate standard compliant email. :(
This message has a Content-Type: header with no subtype. I hope you
can still read it.
-Me

View file

@ -0,0 +1,52 @@
Return-Path: <xx@xx.dk>
Received: from fepD.post.tele.dk (195.41.46.149) by mail.groupcare.dk (LSMTP for Windows NT v1.1b) with SMTP id <0.0014F8A2@mail.groupcare.dk>; Mon, 30 Apr 2001 12:17:50 +0200
User-Agent: Microsoft-Outlook-Express-Macintosh-Edition/5.02.2106
Subject: XX
From: xx@xx.dk
To: XX
Message-ID: <xxxx>
Mime-version: 1.0
Content-type: multipart/mixed;
boundary="MS_Mac_OE_3071477847_720252_MIME_Part"
> Denne meddelelse er i MIME-format. Da dit postl
--MS_Mac_OE_3071477847_720252_MIME_Part
Content-type: multipart/alternative;
boundary="MS_Mac_OE_3071477847_720252_MIME_Part"
--MS_Mac_OE_3071477847_720252_MIME_Part
Content-type: text/plain; charset="ISO-8859-1"
Content-transfer-encoding: quoted-printable
Some removed test.
--MS_Mac_OE_3071477847_720252_MIME_Part
Content-type: text/html; charset="ISO-8859-1"
Content-transfer-encoding: quoted-printable
<HTML>
<HEAD>
<TITLE>Some removed HTML</TITLE>
</HEAD>
<BODY>
Some removed text.
</BODY>
</HTML>
--MS_Mac_OE_3071477847_720252_MIME_Part--
--MS_Mac_OE_3071477847_720252_MIME_Part
Content-type: image/gif; name="xx.gif";
x-mac-creator="6F676C65";
x-mac-type="47494666"
Content-disposition: attachment
Content-transfer-encoding: base64
Some removed base64 encoded chars.
--MS_Mac_OE_3071477847_720252_MIME_Part--

View file

@ -0,0 +1,123 @@
Return-Path: <>
Delivered-To: scr-admin@socal-raves.org
Received: from cougar.noc.ucla.edu (cougar.noc.ucla.edu [169.232.10.18])
by babylon.socal-raves.org (Postfix) with ESMTP id CCC2C51B84
for <scr-admin@socal-raves.org>; Sun, 23 Sep 2001 20:13:54 -0700 (PDT)
Received: from sims-ms-daemon by cougar.noc.ucla.edu
(Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10)
id <0GK500B01D0B8Y@cougar.noc.ucla.edu> for scr-admin@socal-raves.org; Sun,
23 Sep 2001 20:14:35 -0700 (PDT)
Received: from cougar.noc.ucla.edu
(Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10)
id <0GK500B01D0B8X@cougar.noc.ucla.edu>; Sun, 23 Sep 2001 20:14:35 -0700 (PDT)
Date: Sun, 23 Sep 2001 20:14:35 -0700 (PDT)
From: Internet Mail Delivery <postmaster@ucla.edu>
Subject: Delivery Notification: Delivery has failed
To: scr-admin@socal-raves.org
Message-id: <0GK500B04D0B8X@cougar.noc.ucla.edu>
MIME-version: 1.0
Sender: scr-owner@socal-raves.org
Errors-To: scr-owner@socal-raves.org
X-BeenThere: scr@socal-raves.org
X-Mailman-Version: 2.1a3
Precedence: bulk
List-Help: <mailto:scr-request@socal-raves.org?subject=help>
List-Post: <mailto:scr@socal-raves.org>
List-Subscribe: <http://socal-raves.org/mailman/listinfo/scr>,
<mailto:scr-request@socal-raves.org?subject=subscribe>
List-Id: SoCal-Raves <scr.socal-raves.org>
List-Unsubscribe: <http://socal-raves.org/mailman/listinfo/scr>,
<mailto:scr-request@socal-raves.org?subject=unsubscribe>
List-Archive: <http://socal-raves.org/mailman/private/scr/>
Content-Type: multipart/report; boundary="Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)"
--Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)
Content-type: text/plain; charset=ISO-8859-1
This report relates to a message you sent with the following header fields:
Message-id: <002001c144a6$8752e060$56104586@oxy.edu>
Date: Sun, 23 Sep 2001 20:10:55 -0700
From: "Ian T. Henry" <henryi@oxy.edu>
To: SoCal Raves <scr@socal-raves.org>
Subject: [scr] yeah for Ians!!
Your message cannot be delivered to the following recipients:
Recipient address: jangel1@cougar.noc.ucla.edu
Reason: recipient reached disk quota
--Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)
Content-type: message/DELIVERY-STATUS
Original-envelope-id: 0GK500B4HD0888@cougar.noc.ucla.edu
Reporting-MTA: dns; cougar.noc.ucla.edu
Action: failed
Status: 5.0.0 (recipient reached disk quota)
Original-recipient: rfc822;jangel1@cougar.noc.ucla.edu
Final-recipient: rfc822;jangel1@cougar.noc.ucla.edu
--Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)
Content-type: MESSAGE/RFC822
Return-path: scr-admin@socal-raves.org
Received: from sims-ms-daemon by cougar.noc.ucla.edu
(Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10)
id <0GK500B01D0B8X@cougar.noc.ucla.edu>; Sun, 23 Sep 2001 20:14:35 -0700 (PDT)
Received: from panther.noc.ucla.edu by cougar.noc.ucla.edu
(Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10)
with ESMTP id <0GK500B4GD0888@cougar.noc.ucla.edu> for jangel1@sims-ms-daemon;
Sun, 23 Sep 2001 20:14:33 -0700 (PDT)
Received: from babylon.socal-raves.org
(ip-209-85-222-117.dreamhost.com [209.85.222.117])
by panther.noc.ucla.edu (8.9.1a/8.9.1) with ESMTP id UAA09793 for
<jangel1@ucla.edu>; Sun, 23 Sep 2001 20:14:32 -0700 (PDT)
Received: from babylon (localhost [127.0.0.1]) by babylon.socal-raves.org
(Postfix) with ESMTP id D3B2951B70; Sun, 23 Sep 2001 20:13:47 -0700 (PDT)
Received: by babylon.socal-raves.org (Postfix, from userid 60001)
id A611F51B82; Sun, 23 Sep 2001 20:13:46 -0700 (PDT)
Received: from tiger.cc.oxy.edu (tiger.cc.oxy.edu [134.69.3.112])
by babylon.socal-raves.org (Postfix) with ESMTP id ADA7351B70 for
<scr@socal-raves.org>; Sun, 23 Sep 2001 20:13:44 -0700 (PDT)
Received: from ent (n16h86.dhcp.oxy.edu [134.69.16.86])
by tiger.cc.oxy.edu (8.8.8/8.8.8) with SMTP id UAA08100 for
<scr@socal-raves.org>; Sun, 23 Sep 2001 20:14:24 -0700 (PDT)
Date: Sun, 23 Sep 2001 20:10:55 -0700
From: "Ian T. Henry" <henryi@oxy.edu>
Subject: [scr] yeah for Ians!!
Sender: scr-admin@socal-raves.org
To: SoCal Raves <scr@socal-raves.org>
Errors-to: scr-admin@socal-raves.org
Message-id: <002001c144a6$8752e060$56104586@oxy.edu>
MIME-version: 1.0
X-Mailer: Microsoft Outlook Express 5.50.4522.1200
Content-type: text/plain; charset=us-ascii
Precedence: bulk
Delivered-to: scr-post@babylon.socal-raves.org
Delivered-to: scr@socal-raves.org
X-Converted-To-Plain-Text: from multipart/alternative by demime 0.98e
X-Converted-To-Plain-Text: Alternative section used was text/plain
X-BeenThere: scr@socal-raves.org
X-Mailman-Version: 2.1a3
List-Help: <mailto:scr-request@socal-raves.org?subject=help>
List-Post: <mailto:scr@socal-raves.org>
List-Subscribe: <http://socal-raves.org/mailman/listinfo/scr>,
<mailto:scr-request@socal-raves.org?subject=subscribe>
List-Id: SoCal-Raves <scr.socal-raves.org>
List-Unsubscribe: <http://socal-raves.org/mailman/listinfo/scr>,
<mailto:scr-request@socal-raves.org?subject=unsubscribe>
List-Archive: <http://socal-raves.org/mailman/private/scr/>
I always love to find more Ian's that are over 3 years old!!
Ian
_______________________________________________
For event info, list questions, or to unsubscribe, see http://www.socal-raves.org/
--Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)--

View file

@ -0,0 +1,12 @@
MIME-Version: 1.0
From: Barry <barry@digicool.com>
To: Dingus Lovers <cravindogs@cravindogs.com>
Subject: Here is your dingus fish
Date: Fri, 20 Apr 2001 19:35:02 -0400
Content-Type: multipart/mixed; boundary="BOUNDARY"
Hi there,
This is the dingus fish.
[Non-text (image/gif) part of message omitted, filename dingusfish.gif]

View file

@ -0,0 +1,6 @@
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals";
spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"

View file

@ -0,0 +1,43 @@
Send Ppp mailing list submissions to
ppp@zzz.org
To subscribe or unsubscribe via the World Wide Web, visit
http://www.zzz.org/mailman/listinfo/ppp
or, via email, send a message with subject or body 'help' to
ppp-request@zzz.org
You can reach the person managing the list at
ppp-admin@zzz.org
When replying, please edit your Subject line so it is more specific
than "Re: Contents of Ppp digest..."
Today's Topics:
1. testing #1 (Barry A. Warsaw)
2. testing #2 (Barry A. Warsaw)
3. testing #3 (Barry A. Warsaw)
4. testing #4 (Barry A. Warsaw)
5. testing #5 (Barry A. Warsaw)
hello
hello
hello
hello
hello
_______________________________________________
Ppp mailing list
Ppp@zzz.org
http://www.zzz.org/mailman/listinfo/ppp

View file

@ -0,0 +1,22 @@
Return-Path: <bbb@zzz.org>
Delivered-To: bbb@zzz.org
Received: by mail.zzz.org (Postfix, from userid 889)
id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
From: bbb@ddd.com (John X. Doe)
To: bbb@zzz.org
Cc: ccc@zzz.org
CC: ddd@zzz.org
cc: eee@zzz.org
Subject: This is a test message
Date: Fri, 4 May 2001 14:05:44 -0400
Hi,
Do you like this message?
-Me

View file

@ -0,0 +1,20 @@
From: aperson@dom.ain
To: bperson@dom.ain
Subject: Test
Content-Type: multipart/mixed; boundary="BOUNDARY"
MIME message
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
One
--BOUNDARY
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Two
--BOUNDARY--
End of MIME message

View file

@ -0,0 +1,46 @@
Mime-Version: 1.0
Message-Id: <a05001902b7f1c33773e9@[134.84.183.138]>
Date: Tue, 16 Oct 2001 13:59:25 +0300
To: a@example.com
From: b@example.com
Content-Type: multipart/mixed; boundary="============_-1208892523==_============"
--============_-1208892523==_============
Content-Type: text/plain; charset="us-ascii" ; format="flowed"
Text text text.
--============_-1208892523==_============
Content-Id: <a05001902b7f1c33773e9@[134.84.183.138].0.0>
Content-Type: image/jpeg; name="wibble.JPG"
; x-mac-type="4A504547"
; x-mac-creator="474B4F4E"
Content-Disposition: attachment; filename="wibble.JPG"
Content-Transfer-Encoding: base64
/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAALCAXABIEBAREA
g6bCjjw/pIZSjO6FWFpldjySOmCNrO7DBZibUXhTwtCixw+GtAijVdqxxaPp0aKvmGXa
qrbBQvms0mAMeYS/3iTV1dG0hHaRNK01XblnWxtVdjkHLMIgTyqnk9VB7CrP2KzIINpa
4O7I+zxYO9WV8jZg71Zlb+8rMDkEirAVQFAUAKAFAAAUAYAUDgADgY6DjpRtXj5RxjHA
4wQRj0wQCMdCAewpaKKK/9k=
--============_-1208892523==_============
Content-Id: <a05001902b7f1c33773e9@[134.84.183.138].0.1>
Content-Type: image/jpeg; name="wibble2.JPG"
; x-mac-type="4A504547"
; x-mac-creator="474B4F4E"
Content-Disposition: attachment; filename="wibble2.JPG"
Content-Transfer-Encoding: base64
/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAALCAXABJ0BAREA
/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA
W6NFJJBEkU10kKGTcWMDwxuU+0JHvk8qAtOpNwqSR0n8c3BlDyXHlqsUltHEiTvdXLxR
7vMiGDNJAJWkAMk8ZkCFp5G2oo5W++INrbQtNfTQxJAuXlupz9oS4d5Y1W+E2XlWZJJE
Y7LWYQxTLE1zuMbfBPxw8X2fibVdIbSbI6nLZxX635t9TjtYreWR7WGKJTLJFFKSlozO
0ShxIXM43uC3/9k=
--============_-1208892523==_============
Content-Type: text/plain; charset="us-ascii" ; format="flowed"
Text text text.
--============_-1208892523==_============--

View file

@ -0,0 +1,8 @@
From: aperson@dom.ain
Content-Type: multipart/mixed; boundary="BOUNDARY"
--BOUNDARY
Content-Type: text/plain
A message part
--BOUNDARY--

View file

@ -0,0 +1,10 @@
Content-Type: multipart/mixed; boundary="BOUNDARY"
MIME-Version: 1.0
Subject: A subject
To: aperson@dom.ain
From: bperson@dom.ain
--BOUNDARY
--BOUNDARY--

View file

@ -0,0 +1,117 @@
From MAILER-DAEMON Fri Apr 06 16:46:09 2001
Received: from [204.245.199.98] (helo=zinfandel.lacita.com)
by www.linux.org.uk with esmtp (Exim 3.13 #1)
id 14lYR6-0008Iv-00
for linuxuser-admin@www.linux.org.uk; Fri, 06 Apr 2001 16:46:09 +0100
Received: from localhost (localhost) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with internal id JAB03225; Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800)
Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800)
From: Mail Delivery Subsystem <MAILER-DAEMON@zinfandel.lacita.com>
Subject: Returned mail: Too many hops 19 (17 max): from <linuxuser-admin@www.linux.org.uk> via [199.164.235.226], to <scoffman@wellpartner.com>
Message-Id: <200104061723.JAB03225@zinfandel.lacita.com>
To: <linuxuser-admin@www.linux.org.uk>
To: postmaster@zinfandel.lacita.com
MIME-Version: 1.0
Content-Type: multipart/report; report-type=delivery-status;
bo
Auto-Submitted: auto-generated (failure)
This is a MIME-encapsulated message
--JAB03225.986577786/zinfandel.lacita.com
The original message was received at Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800)
from [199.164.235.226]
----- The following addresses have delivery notifications -----
<scoffman@wellpartner.com> (unrecoverable error)
----- Transcript of session follows -----
554 Too many hops 19 (17 max): from <linuxuser-admin@www.linux.org.uk> via [199.164.235.226], to <scoffman@wellpartner.com>
--JAB03225.986577786/zinfandel.lacita.com
Content-Type: message/delivery-status
Reporting-MTA: dns; zinfandel.lacita.com
Received-From-MTA: dns; [199.164.235.226]
Arrival-Date: Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800)
Final-Recipient: rfc822; scoffman@wellpartner.com
Action: failed
Status: 5.4.6
Last-Attempt-Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800)
--JAB03225.986577786/zinfandel.lacita.com
Content-Type: text/rfc822-headers
Return-Path: linuxuser-admin@www.linux.org.uk
Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03225 for <scoffman@wellpartner.com>; Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800)
Received: from zinfandel.lacita.com ([204.245.199.98])
by
fo
Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03221 for <scoffman@wellpartner.com>; Fri, 6 Apr 2001 09:22:18 -0800 (GMT-0800)
Received: from zinfandel.lacita.com ([204.245.199.98])
by
fo
Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03217 for <scoffman@wellpartner.com>; Fri, 6 Apr 2001 09:21:37 -0800 (GMT-0800)
Received: from zinfandel.lacita.com ([204.245.199.98])
by
fo
Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03213 for <scoffman@wellpartner.com>; Fri, 6 Apr 2001 09:20:56 -0800 (GMT-0800)
Received: from zinfandel.lacita.com ([204.245.199.98])
by
fo
Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03209 for <scoffman@wellpartner.com>; Fri, 6 Apr 2001 09:20:15 -0800 (GMT-0800)
Received: from zinfandel.lacita.com ([204.245.199.98])
by
fo
Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03205 for <scoffman@wellpartner.com>; Fri, 6 Apr 2001 09:19:33 -0800 (GMT-0800)
Received: from zinfandel.lacita.com ([204.245.199.98])
by
fo
Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03201 for <scoffman@wellpartner.com>; Fri, 6 Apr 2001 09:18:52 -0800 (GMT-0800)
Received: from zinfandel.lacita.com ([204.245.199.98])
by
fo
Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03197 for <scoffman@wellpartner.com>; Fri, 6 Apr 2001 09:17:54 -0800 (GMT-0800)
Received: from www.linux.org.uk (parcelfarce.linux.theplanet.co.uk [195.92.249.252])
by
fo
Received: from localhost.localdomain
([
by
id
Received: from [212.1.130.11] (helo=s1.uklinux.net ident=root)
by
id
fo
Received: from server (ppp-2-22.cvx4.telinco.net [212.1.149.22])
by
fo
From: Daniel James <daniel@linuxuser.co.uk>
Organization: LinuxUser
To: linuxuser@www.linux.org.uk
X-Mailer: KMail [version 1.1.99]
Content-Type: text/plain;
c
MIME-Version: 1.0
Message-Id: <01040616033903.00962@server>
Content-Transfer-Encoding: 8bit
Subject: [LinuxUser] bulletin no. 45
Sender: linuxuser-admin@www.linux.org.uk
Errors-To: linuxuser-admin@www.linux.org.uk
X-BeenThere: linuxuser@www.linux.org.uk
X-Mailman-Version: 2.0.3
Precedence: bulk
List-Help: <mailto:linuxuser-request@www.linux.org.uk?subject=help>
List-Post: <mailto:linuxuser@www.linux.org.uk>
List-Subscribe: <http://www.linux.org.uk/mailman/listinfo/linuxuser>,
<m
List-Id: bulletins from LinuxUser magazine <linuxuser.www.linux.org.uk>
List-Unsubscribe: <http://www.linux.org.uk/mailman/listinfo/linuxuser>,
<m
List-Archive: <http://www.linux.org.uk/pipermail/linuxuser/>
Date: Fri, 6 Apr 2001 16:03:39 +0100
--JAB03225.986577786/zinfandel.lacita.com--

View file

@ -0,0 +1,46 @@
Received: from xcar [192.168.0.2] by jeeves.wooster.local
(SMTPD32-7.07 EVAL) id AFF92F0214; Sun, 12 May 2002 08:55:37 +0100
Date: Sun, 12 May 2002 08:56:15 +0100
From: Father Time <father.time@xcar.wooster.local>
To: timbo@jeeves.wooster.local
Subject: IMAP file test
Message-ID: <6df65d354b.father.time@rpc.wooster.local>
X-Organization: Home
User-Agent: Messenger-Pro/2.50a (MsgServe/1.50) (RISC-OS/4.02) POPstar/2.03
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="1618492860--2051301190--113853680"
Status: R
X-UIDL: 319998302
This message is in MIME format which your mailer apparently does not support.
You either require a newer version of your software which supports MIME, or
a separate MIME decoding utility. Alternatively, ask the sender of this
message to resend it in a different format.
--1618492860--2051301190--113853680
Content-Type: text/plain; charset=us-ascii
Simple email with attachment.
--1618492860--2051301190--113853680
Content-Type: application/riscos; name="clock.bmp,69c"; type=BMP;
load=&fff69c4b; exec=&355dd4d1; access=&03
Content-Disposition: attachment; filename="clock.bmp"
Content-Transfer-Encoding: base64
Qk12AgAAAAAAAHYAAAAoAAAAIAAAACAAAAABAAQAAAAAAAAAAADXDQAA1w0AAAAAAAAA
AAAAAAAAAAAAiAAAiAAAAIiIAIgAAACIAIgAiIgAALu7uwCIiIgAERHdACLuIgAz//8A
zAAAAN0R3QDu7iIA////AAAAAAAAAAAAAAAAAAAAAAAAAAi3AAAAAAAAADeAAAAAAAAA
C3ADMzMzMANwAAAAAAAAAAAHMAAAAANwAAAAAAAAAACAMAd3zPfwAwgAAAAAAAAIAwd/
f8x/f3AwgAAAAAAAgDB0x/f3//zPAwgAAAAAAAcHfM9////8z/AwAAAAAAiwd/f3////
////A4AAAAAAcEx/f///////zAMAAAAAiwfM9////3///8zwOAAAAAcHf3////B/////
8DAAAAALB/f3///wd3d3//AwAAAABwTPf//wCQAAD/zAMAAAAAsEx/f///B////8wDAA
AAAHB39////wf/////AwAAAACwf39///8H/////wMAAAAIcHfM9///B////M8DgAAAAA
sHTH///wf///xAMAAAAACHB3f3//8H////cDgAAAAAALB3zH//D//M9wMAAAAAAAgLB0
z39///xHAwgAAAAAAAgLB3d3RHd3cDCAAAAAAAAAgLAHd0R3cAMIAAAAAAAAgAgLcAAA
AAMwgAgAAAAACDAAAAu7t7cwAAgDgAAAAABzcIAAAAAAAAgDMwAAAAAAN7uwgAAAAAgH
MzMAAAAACH97tzAAAAALu3c3gAAAAAAL+7tzDABAu7f7cAAAAAAACA+3MA7EQAv/sIAA
AAAAAAAIAAAAAAAAAIAAAAAA
--1618492860--2051301190--113853680--

View file

@ -0,0 +1,15 @@
Return-Path: <aperson@dom.ain>
Received: by mail.dom.ain (Postfix, from userid 889)
id B9D0AD35DB; Tue, 4 Jun 2002 21:46:59 -0400 (EDT)
Message-ID: <15613.28051.707126.569693@dom.ain>
Date: Tue, 4 Jun 2002 21:46:59 -0400
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Subject: bug demonstration
12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
more text
From: aperson@dom.ain (Anne P. Erson)
To: bperson@dom.ain (Barney P. Erson)
test

View file

@ -0,0 +1,25 @@
From: aperson@dom.ain
MIME-Version: 1.0
Content-Type: multipart/digest; boundary=BOUNDARY
--BOUNDARY
Content-Type: message/rfc822
Content-Type: text/plain; charset=us-ascii
To: aa@bb.org
From: cc@dd.org
Subject: ee
message 1
--BOUNDARY
Content-Type: message/rfc822
Content-Type: text/plain; charset=us-ascii
To: aa@bb.org
From: cc@dd.org
Subject: ee
message 2
--BOUNDARY--

View file

@ -0,0 +1,22 @@
Return-Path: <bbb@zzz.org>
Delivered-To: bbb@zzz.org
Received: by mail.zzz.org (Postfix, from userid 889)
id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii;
title*0*="us-ascii'en'This%20is%20even%20more%20";
title*1*="%2A%2A%2Afun%2A%2A%2A%20";
title*2="isn't it!"
Content-Transfer-Encoding: 7bit
Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
From: bbb@ddd.com (John X. Doe)
To: bbb@zzz.org
Subject: This is a test message
Date: Fri, 4 May 2001 14:05:44 -0400
Hi,
Do you like this message?
-Me

View file

@ -0,0 +1,23 @@
From: aperson@dom.ain
MIME-Version: 1.0
Content-Type: multipart/digest; boundary=BOUNDARY
--BOUNDARY
Content-Type: text/plain; charset=us-ascii
To: aa@bb.org
From: cc@dd.org
Subject: ee
message 1
--BOUNDARY
Content-Type: text/plain; charset=us-ascii
To: aa@bb.org
From: cc@dd.org
Subject: ee
message 2
--BOUNDARY--

View file

@ -0,0 +1,15 @@
From: aperson@dom.ain
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=BOUNDARY_
--BOUNDARY
Content-Type: text/plain
message 1
--BOUNDARY
Content-Type: text/plain
message 2
--BOUNDARY--

View file

@ -0,0 +1,14 @@
Delivered-To: freebsd-isp@freebsd.org
Date: Tue, 26 Sep 2000 12:23:03 -0500
From: Anne Person <aperson@example.com>
To: Barney Dude <bdude@example.com>
Subject: Re: Limiting Perl CPU Utilization...
Mime-Version: 1.0
Content-Type: text/plain; charset*=ansi-x3.4-1968''us-ascii
Content-Disposition: inline
User-Agent: Mutt/1.3.8i
Sender: owner-freebsd-isp@FreeBSD.ORG
Precedence: bulk
X-Loop: FreeBSD.org
Some message.

View file

@ -0,0 +1,29 @@
Delivered-To: freebsd-isp@freebsd.org
Date: Wed, 27 Sep 2000 11:11:09 -0500
From: Anne Person <aperson@example.com>
To: Barney Dude <bdude@example.com>
Subject: Re: Limiting Perl CPU Utilization...
Mime-Version: 1.0
Content-Type: multipart/signed; micalg*=ansi-x3.4-1968''pgp-md5;
protocol*=ansi-x3.4-1968''application%2Fpgp-signature;
boundary*="ansi-x3.4-1968''EeQfGwPcQSOJBaQU"
Content-Disposition: inline
Sender: owner-freebsd-isp@FreeBSD.ORG
Precedence: bulk
X-Loop: FreeBSD.org
--EeQfGwPcQSOJBaQU
Content-Type: text/plain; charset*=ansi-x3.4-1968''us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
part 1
--EeQfGwPcQSOJBaQU
Content-Type: text/plain
Content-Disposition: inline
part 2
--EeQfGwPcQSOJBaQU--

View file

@ -0,0 +1,19 @@
From: aperson@dom.ain
To: bperson@dom.ain
Content-Type: multipart/digest; boundary=XYZ
--XYZ
Content-Type: text/plain
This is a text plain part that is counter to recommended practice in
RFC 2046, $5.1.5, but is not illegal
--XYZ
From: cperson@dom.ain
To: dperson@dom.ain
A submessage
--XYZ--

View file

@ -0,0 +1,4 @@
From: aperson@dom.ain
To: bperson@dom.ain
Subject: here's something interesting
counter to RFC 2822, there's no separating newline here

View file

@ -0,0 +1,40 @@
Mime-Version: 1.0
Content-Type: Multipart/Mixed; Boundary="NextPart"
To: IETF-Announce:;
From: Internet-Drafts@ietf.org
Subject: I-D ACTION:draft-ietf-mboned-mix-00.txt
Date: Tue, 22 Dec 1998 16:55:06 -0500
--NextPart
Blah blah blah
--NextPart
Content-Type: Multipart/Alternative; Boundary="OtherAccess"
--OtherAccess
Content-Type: Message/External-body;
access-type="mail-server";
server="mailserv@ietf.org"
Content-Type: text/plain
Content-ID: <19981222151406.I-D@ietf.org>
ENCODING mime
FILE /internet-drafts/draft-ietf-mboned-mix-00.txt
--OtherAccess
Content-Type: Message/External-body;
name="draft-ietf-mboned-mix-00.txt";
site="ftp.ietf.org";
access-type="anon-ftp";
directory="internet-drafts"
Content-Type: text/plain
Content-ID: <19981222151406.I-D@ietf.org>
--OtherAccess--
--NextPart--

View file

@ -0,0 +1,22 @@
Content-Type: multipart/mixed; boundary=ABCDE
--ABCDE
Content-Type: text/x-one
Blah
--ABCDE
--ABCDE
Content-Type: text/x-two
Blah
--ABCDE
--ABCDE
--ABCDE
--ABCDE
Content-Type: text/x-two
Blah
--ABCDE--

View file

@ -0,0 +1,101 @@
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
------- =_aaaaaaaaaa0
Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa1"
Content-ID: <20592.1022586929.1@example.com>
------- =_aaaaaaaaaa1
Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa2"
Content-ID: <20592.1022586929.2@example.com>
------- =_aaaaaaaaaa2
Content-Type: text/plain
Content-ID: <20592.1022586929.3@example.com>
Content-Description: very tricky
Content-Transfer-Encoding: 7bit
Unlike the test test_nested-multiples-with-internal-boundary, this
piece of text not only contains the outer boundary tags
------- =_aaaaaaaaaa1
and
------- =_aaaaaaaaaa0
but puts them at the start of a line! And, to be even nastier, it
even includes a couple of end tags, such as this one:
------- =_aaaaaaaaaa1--
and this one, which is from a multipart we haven't even seen yet!
------- =_aaaaaaaaaa4--
This will, I'm sure, cause much breakage of MIME parsers. But, as
far as I can tell, it's perfectly legal. I have not yet ever seen
a case of this in the wild, but I've seen *similar* things.
------- =_aaaaaaaaaa2
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.4@example.com>
Content-Description: patch2
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa2--
------- =_aaaaaaaaaa1
Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa3"
Content-ID: <20592.1022586929.6@example.com>
------- =_aaaaaaaaaa3
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.7@example.com>
Content-Description: patch3
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa3
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.8@example.com>
Content-Description: patch4
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa3--
------- =_aaaaaaaaaa1
Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa4"
Content-ID: <20592.1022586929.10@example.com>
------- =_aaaaaaaaaa4
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.11@example.com>
Content-Description: patch5
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa4
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.12@example.com>
Content-Description: patch6
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa4--
------- =_aaaaaaaaaa1--
------- =_aaaaaaaaaa0
Content-Type: text/plain; charset="us-ascii"
Content-ID: <20592.1022586929.15@example.com>
--
It's never too late to have a happy childhood.
------- =_aaaaaaaaaa0--

View file

@ -0,0 +1,83 @@
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
------- =_aaaaaaaaaa0
Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa1"
Content-ID: <20592.1022586929.1@example.com>
------- =_aaaaaaaaaa1
Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1"
Content-ID: <20592.1022586929.2@example.com>
------- =_aaaaaaaaaa1
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.3@example.com>
Content-Description: patch1
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa1
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.4@example.com>
Content-Description: patch2
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa1--
------- =_aaaaaaaaaa1
Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1"
Content-ID: <20592.1022586929.6@example.com>
------- =_aaaaaaaaaa1
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.7@example.com>
Content-Description: patch3
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa1
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.8@example.com>
Content-Description: patch4
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa1--
------- =_aaaaaaaaaa1
Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1"
Content-ID: <20592.1022586929.10@example.com>
------- =_aaaaaaaaaa1
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.11@example.com>
Content-Description: patch5
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa1
Content-Type: application/octet-stream
Content-ID: <20592.1022586929.12@example.com>
Content-Description: patch6
Content-Transfer-Encoding: base64
XXX
------- =_aaaaaaaaaa1--
------- =_aaaaaaaaaa1--
------- =_aaaaaaaaaa0
Content-Type: text/plain; charset="us-ascii"
Content-ID: <20592.1022586929.15@example.com>
--
It's never too late to have a happy childhood.
------- =_aaaaaaaaaa0--

View file

@ -0,0 +1,10 @@
MIME-Version: 1.0
Content-Type: text/html; boundary="--961284236552522269"
----961284236552522269
Content-Type: text/html;
Content-Transfer-Encoding: 7Bit
<html></html>
----961284236552522269--

View file

@ -0,0 +1,8 @@
From: "Allison Dunlap" <xxx@example.com>
To: yyy@example.com
Subject: 64423
Date: Sun, 11 Jul 2004 16:09:27 -0300
MIME-Version: 1.0
Content-Type: multipart/alternative;
Blah blah blah

View file

@ -0,0 +1,20 @@
Content-Type: multipart/mixed; boundary="AAA"
From: Mail Delivery Subsystem <xxx@example.com>
To: yyy@example.com
This is a MIME-encapsulated message
--AAA
Stuff
--AAA
Content-Type: message/rfc822
From: webmaster@python.org
To: zzz@example.com
Content-Type: multipart/mixed; boundary="BBB"
--BBB--
--AAA--

View file

@ -0,0 +1,217 @@
From SRS0=aO/p=ON=bag.python.org=None@bounce2.pobox.com Fri Nov 26 21:40:36 2004
X-VM-v5-Data: ([nil nil nil nil nil nil nil nil nil]
[nil nil nil nil nil nil nil "MAILER DAEMON <>" "MAILER DAEMON <>" nil nil "Banned file: auto__mail.python.bat in mail from you" "^From:" nil nil nil nil "Banned file: auto__mail.python.bat in mail from you" nil nil nil nil nil nil nil]
nil)
MIME-Version: 1.0
Message-Id: <edab.7804f5cb8070@python.org>
Content-Type: multipart/report; report-type=delivery-status;
charset=utf-8;
boundary="----------=_1101526904-1956-5"
X-Virus-Scanned: by XS4ALL Virus Scanner
X-UIDL: 4\G!!!<c"!UV["!M7C!!
From: MAILER DAEMON <>
To: <webmaster@python.org>
Subject: Banned file: auto__mail.python.bat in mail from you
Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
This is a multi-part message in MIME format...
------------=_1101526904-1956-5
Content-Type: text/plain; charset="utf-8"
Content-Disposition: inline
Content-Transfer-Encoding: 7bit
BANNED FILENAME ALERT
Your message to: xxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxx@dot.ca.gov, xxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxxxxx@dot.ca.gov
was blocked by our Spam Firewall. The email you sent with the following subject has NOT BEEN DELIVERED:
Subject: Delivery_failure_notice
An attachment in that mail was of a file type that the Spam Firewall is set to block.
------------=_1101526904-1956-5
Content-Type: message/delivery-status
Content-Disposition: inline
Content-Transfer-Encoding: 7bit
Content-Description: Delivery error report
Reporting-MTA: dns; sacspam01.dot.ca.gov
Received-From-MTA: smtp; sacspam01.dot.ca.gov ([127.0.0.1])
Arrival-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
------------=_1101526904-1956-5
Content-Type: text/rfc822-headers
Content-Disposition: inline
Content-Transfer-Encoding: 7bit
Content-Description: Undelivered-message headers
Received: from kgsav.org (ppp-70-242-162-63.dsl.spfdmo.swbell.net [70.242.162.63])
by sacspam01.dot.ca.gov (Spam Firewall) with SMTP
id A232AD03DE3A; Fri, 26 Nov 2004 19:41:35 -0800 (PST)
From: webmaster@python.org
To: xxxxx@dot.ca.gov
Date: Sat, 27 Nov 2004 03:35:30 UTC
Subject: Delivery_failure_notice
Importance: Normal
X-Priority: 3 (Normal)
X-MSMail-Priority: Normal
Message-ID: <edab.7804f5cb8070@python.org>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="====67bd2b7a5.f99f7"
Content-Transfer-Encoding: 7bit
------------=_1101526904-1956-5--

View file

@ -0,0 +1,33 @@
Return-Path: <barry@python.org>
Delivered-To: barry@python.org
Received: by mail.python.org (Postfix, from userid 889)
id C2BF0D37C6; Tue, 11 Sep 2001 00:05:05 -0400 (EDT)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="h90VIIIKmx"
Content-Transfer-Encoding: 7bit
Message-ID: <15261.36209.358846.118674@anthem.python.org>
From: barry@python.org (Barry A. Warsaw)
To: barry@python.org
Subject: a simple multipart
Date: Tue, 11 Sep 2001 00:05:05 -0400
X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid
X-Attribution: BAW
X-Oblique-Strategy: Make a door into a window
--h90VIIIKmx
Content-Type: text/plain; name="msg.txt"
Content-Transfer-Encoding: 7bit
a simple kind of mirror
to reflect upon our own
--h90VIIIKmx
Content-Type: text/plain; name="msg.txt"
Content-Transfer-Encoding: 7bit
a simple kind of mirror
to reflect upon our own
--h90VIIIKmx--

View file

@ -0,0 +1,33 @@
From: <foo@bar.baz>
To: <baz@bar.foo>
Subject: test
X-Long-Line: Some really long line contains a lot of text and thus has to be rewrapped because it is some
really long
line
MIME-Version: 1.0
Content-Type: multipart/signed; boundary="borderline";
protocol="application/pgp-signature"; micalg=pgp-sha1
This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
--borderline
Content-Type: text/plain
X-Long-Line: Another really long line contains a lot of text and thus has to be rewrapped because it is another
really long
line
This is the signed contents.
--borderline
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.6 (GNU/Linux)
iD8DBQFG03voRhp6o4m9dFsRApSZAKCCAN3IkJlVRg6NvAiMHlvvIuMGPQCeLZtj
FGwfnRHFBFO/S4/DKysm0lI=
=t7+s
-----END PGP SIGNATURE-----
--borderline--

View file

@ -0,0 +1,23 @@
Return-Path: <sender@example.net>
Delivery-Date: Mon, 08 Feb 2010 14:05:16 +0100
Received: from example.org (example.org [64.5.53.58])
by example.net (node=mxbap2) with ESMTP (Nemesis)
id UNIQUE for someone@example.com; Mon, 08 Feb 2010 14:05:16 +0100
Date: Mon, 01 Feb 2010 12:21:16 +0100
From: "Sender" <sender@example.net>
To: <someone@example.com>
Subject: GroupwiseForwardingTest
Mime-Version: 1.0
Content-Type: message/rfc822
Return-path: <sender@example.net>
Message-ID: <4B66B890.4070408@teconcept.de>
Date: Mon, 01 Feb 2010 12:18:40 +0100
From: "Dr. Sender" <sender@example.net>
MIME-Version: 1.0
To: "Recipient" <recipient@example.com>
Subject: GroupwiseForwardingTest
Content-Type: text/plain; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit
Testing email forwarding with Groupwise 1.2.2010

View file

@ -0,0 +1,198 @@
import unittest
from email import _encoded_words as _ew
from email import errors
from test.test_email import TestEmailBase
class TestDecodeQ(TestEmailBase):
def _test(self, source, ex_result, ex_defects=[]):
result, defects = _ew.decode_q(source)
self.assertEqual(result, ex_result)
self.assertDefectsEqual(defects, ex_defects)
def test_no_encoded(self):
self._test(b'foobar', b'foobar')
def test_spaces(self):
self._test(b'foo=20bar=20', b'foo bar ')
self._test(b'foo_bar_', b'foo bar ')
def test_run_of_encoded(self):
self._test(b'foo=20=20=21=2Cbar', b'foo !,bar')
class TestDecodeB(TestEmailBase):
def _test(self, source, ex_result, ex_defects=[]):
result, defects = _ew.decode_b(source)
self.assertEqual(result, ex_result)
self.assertDefectsEqual(defects, ex_defects)
def test_simple(self):
self._test(b'Zm9v', b'foo')
def test_missing_padding(self):
# 1 missing padding character
self._test(b'dmk', b'vi', [errors.InvalidBase64PaddingDefect])
# 2 missing padding characters
self._test(b'dg', b'v', [errors.InvalidBase64PaddingDefect])
def test_invalid_character(self):
self._test(b'dm\x01k===', b'vi', [errors.InvalidBase64CharactersDefect])
def test_invalid_character_and_bad_padding(self):
self._test(b'dm\x01k', b'vi', [errors.InvalidBase64CharactersDefect,
errors.InvalidBase64PaddingDefect])
def test_invalid_length(self):
self._test(b'abcde', b'abcde', [errors.InvalidBase64LengthDefect])
class TestDecode(TestEmailBase):
def test_wrong_format_input_raises(self):
with self.assertRaises(ValueError):
_ew.decode('=?badone?=')
with self.assertRaises(ValueError):
_ew.decode('=?')
with self.assertRaises(ValueError):
_ew.decode('')
def _test(self, source, result, charset='us-ascii', lang='', defects=[]):
res, char, l, d = _ew.decode(source)
self.assertEqual(res, result)
self.assertEqual(char, charset)
self.assertEqual(l, lang)
self.assertDefectsEqual(d, defects)
def test_simple_q(self):
self._test('=?us-ascii?q?foo?=', 'foo')
def test_simple_b(self):
self._test('=?us-ascii?b?dmk=?=', 'vi')
def test_q_case_ignored(self):
self._test('=?us-ascii?Q?foo?=', 'foo')
def test_b_case_ignored(self):
self._test('=?us-ascii?B?dmk=?=', 'vi')
def test_non_trivial_q(self):
self._test('=?latin-1?q?=20F=fcr=20Elise=20?=', ' Für Elise ', 'latin-1')
def test_q_escaped_bytes_preserved(self):
self._test(b'=?us-ascii?q?=20\xACfoo?='.decode('us-ascii',
'surrogateescape'),
' \uDCACfoo',
defects = [errors.UndecodableBytesDefect])
def test_b_undecodable_bytes_ignored_with_defect(self):
self._test(b'=?us-ascii?b?dm\xACk?='.decode('us-ascii',
'surrogateescape'),
'vi',
defects = [
errors.InvalidBase64CharactersDefect,
errors.InvalidBase64PaddingDefect])
def test_b_invalid_bytes_ignored_with_defect(self):
self._test('=?us-ascii?b?dm\x01k===?=',
'vi',
defects = [errors.InvalidBase64CharactersDefect])
def test_b_invalid_bytes_incorrect_padding(self):
self._test('=?us-ascii?b?dm\x01k?=',
'vi',
defects = [
errors.InvalidBase64CharactersDefect,
errors.InvalidBase64PaddingDefect])
def test_b_padding_defect(self):
self._test('=?us-ascii?b?dmk?=',
'vi',
defects = [errors.InvalidBase64PaddingDefect])
def test_nonnull_lang(self):
self._test('=?us-ascii*jive?q?test?=', 'test', lang='jive')
def test_unknown_8bit_charset(self):
self._test('=?unknown-8bit?q?foo=ACbar?=',
b'foo\xacbar'.decode('ascii', 'surrogateescape'),
charset = 'unknown-8bit',
defects = [])
def test_unknown_charset(self):
self._test('=?foobar?q?foo=ACbar?=',
b'foo\xacbar'.decode('ascii', 'surrogateescape'),
charset = 'foobar',
# XXX Should this be a new Defect instead?
defects = [errors.CharsetError])
def test_q_nonascii(self):
self._test('=?utf-8?q?=C3=89ric?=',
'Éric',
charset='utf-8')
class TestEncodeQ(TestEmailBase):
def _test(self, src, expected):
self.assertEqual(_ew.encode_q(src), expected)
def test_all_safe(self):
self._test(b'foobar', 'foobar')
def test_spaces(self):
self._test(b'foo bar ', 'foo_bar_')
def test_run_of_encodables(self):
self._test(b'foo ,,bar', 'foo__=2C=2Cbar')
class TestEncodeB(TestEmailBase):
def test_simple(self):
self.assertEqual(_ew.encode_b(b'foo'), 'Zm9v')
def test_padding(self):
self.assertEqual(_ew.encode_b(b'vi'), 'dmk=')
class TestEncode(TestEmailBase):
def test_q(self):
self.assertEqual(_ew.encode('foo', 'utf-8', 'q'), '=?utf-8?q?foo?=')
def test_b(self):
self.assertEqual(_ew.encode('foo', 'utf-8', 'b'), '=?utf-8?b?Zm9v?=')
def test_auto_q(self):
self.assertEqual(_ew.encode('foo', 'utf-8'), '=?utf-8?q?foo?=')
def test_auto_q_if_short_mostly_safe(self):
self.assertEqual(_ew.encode('vi.', 'utf-8'), '=?utf-8?q?vi=2E?=')
def test_auto_b_if_enough_unsafe(self):
self.assertEqual(_ew.encode('.....', 'utf-8'), '=?utf-8?b?Li4uLi4=?=')
def test_auto_b_if_long_unsafe(self):
self.assertEqual(_ew.encode('vi.vi.vi.vi.vi.', 'utf-8'),
'=?utf-8?b?dmkudmkudmkudmkudmku?=')
def test_auto_q_if_long_mostly_safe(self):
self.assertEqual(_ew.encode('vi vi vi.vi ', 'utf-8'),
'=?utf-8?q?vi_vi_vi=2Evi_?=')
def test_utf8_default(self):
self.assertEqual(_ew.encode('foo'), '=?utf-8?q?foo?=')
def test_lang(self):
self.assertEqual(_ew.encode('foo', lang='jive'), '=?utf-8*jive?q?foo?=')
def test_unknown_8bit(self):
self.assertEqual(_ew.encode('foo\uDCACbar', charset='unknown-8bit'),
'=?unknown-8bit?q?foo=ACbar?=')
if __name__ == '__main__':
unittest.main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,81 @@
# Copyright (C) 2002-2006 Python Software Foundation
# Contact: email-sig@python.org
# email package unit tests for (optional) Asian codecs
import unittest
from test.test_email import TestEmailBase
from email.charset import Charset
from email.header import Header, decode_header
from email.message import Message
# We're compatible with Python 2.3, but it doesn't have the built-in Asian
# codecs, so we have to skip all these tests.
try:
str(b'foo', 'euc-jp')
except LookupError:
raise unittest.SkipTest
class TestEmailAsianCodecs(TestEmailBase):
def test_japanese_codecs(self):
eq = self.ndiffAssertEqual
jcode = "euc-jp"
gcode = "iso-8859-1"
j = Charset(jcode)
g = Charset(gcode)
h = Header("Hello World!")
jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc'
b'\xa5\xeb\xa5\xc9\xa1\xaa', jcode)
ghello = str(b'Gr\xfc\xdf Gott!', gcode)
h.append(jhello, j)
h.append(ghello, g)
# BAW: This used to -- and maybe should -- fold the two iso-8859-1
# chunks into a single encoded word. However it doesn't violate the
# standard to have them as two encoded chunks and maybe it's
# reasonable <wink> for each .append() call to result in a separate
# encoded word.
eq(h.encode(), """\
Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?=
=?iso-8859-1?q?Gr=FC=DF_Gott!?=""")
eq(decode_header(h.encode()),
[(b'Hello World! ', None),
(b'\x1b$B%O%m!<%o!<%k%I!*\x1b(B', 'iso-2022-jp'),
(b'Gr\xfc\xdf Gott!', gcode)])
subject_bytes = (b'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5'
b'\xa4\xec\xa4\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2'
b'\xf1\xbc\xd4\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3'
b'\xa4\xc6\xa4\xa4\xa4\xde\xa4\xb9')
subject = str(subject_bytes, jcode)
h = Header(subject, j, header_name="Subject")
# test a very long header
enc = h.encode()
# TK: splitting point may differ by codec design and/or Header encoding
eq(enc , """\
=?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKGyhC?=
=?iso-2022-jp?b?GyRCMnE8VCROPjVHJyRyQlQkQyRGJCQkXiQ5GyhC?=""")
# TK: full decode comparison
eq(str(h).encode(jcode), subject_bytes)
def test_payload_encoding_utf8(self):
jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc'
b'\xa5\xeb\xa5\xc9\xa1\xaa', 'euc-jp')
msg = Message()
msg.set_payload(jhello, 'utf-8')
ustr = msg.get_payload(decode=True).decode(msg.get_content_charset())
self.assertEqual(jhello, ustr)
def test_payload_encoding(self):
jcode = 'euc-jp'
jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc'
b'\xa5\xeb\xa5\xc9\xa1\xaa', jcode)
msg = Message()
msg.set_payload(jhello, jcode)
ustr = msg.get_payload(decode=True).decode(msg.get_content_charset())
self.assertEqual(jhello, ustr)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,796 @@
import unittest
from test.test_email import TestEmailBase, parameterize
import textwrap
from email import policy
from email.message import EmailMessage
from email.contentmanager import ContentManager, raw_data_manager
@parameterize
class TestContentManager(TestEmailBase):
policy = policy.default
message = EmailMessage
get_key_params = {
'full_type': (1, 'text/plain',),
'maintype_only': (2, 'text',),
'null_key': (3, '',),
}
def get_key_as_get_content_key(self, order, key):
def foo_getter(msg, foo=None):
bar = msg['X-Bar-Header']
return foo, bar
cm = ContentManager()
cm.add_get_handler(key, foo_getter)
m = self._make_message()
m['Content-Type'] = 'text/plain'
m['X-Bar-Header'] = 'foo'
self.assertEqual(cm.get_content(m, foo='bar'), ('bar', 'foo'))
def get_key_as_get_content_key_order(self, order, key):
def bar_getter(msg):
return msg['X-Bar-Header']
def foo_getter(msg):
return msg['X-Foo-Header']
cm = ContentManager()
cm.add_get_handler(key, foo_getter)
for precedence, key in self.get_key_params.values():
if precedence > order:
cm.add_get_handler(key, bar_getter)
m = self._make_message()
m['Content-Type'] = 'text/plain'
m['X-Bar-Header'] = 'bar'
m['X-Foo-Header'] = 'foo'
self.assertEqual(cm.get_content(m), ('foo'))
def test_get_content_raises_if_unknown_mimetype_and_no_default(self):
cm = ContentManager()
m = self._make_message()
m['Content-Type'] = 'text/plain'
with self.assertRaisesRegex(KeyError, 'text/plain'):
cm.get_content(m)
class BaseThing(str):
pass
baseobject_full_path = __name__ + '.' + 'TestContentManager.BaseThing'
class Thing(BaseThing):
pass
testobject_full_path = __name__ + '.' + 'TestContentManager.Thing'
set_key_params = {
'type': (0, Thing,),
'full_path': (1, testobject_full_path,),
'qualname': (2, 'TestContentManager.Thing',),
'name': (3, 'Thing',),
'base_type': (4, BaseThing,),
'base_full_path': (5, baseobject_full_path,),
'base_qualname': (6, 'TestContentManager.BaseThing',),
'base_name': (7, 'BaseThing',),
'str_type': (8, str,),
'str_full_path': (9, 'builtins.str',),
'str_name': (10, 'str',), # str name and qualname are the same
'null_key': (11, None,),
}
def set_key_as_set_content_key(self, order, key):
def foo_setter(msg, obj, foo=None):
msg['X-Foo-Header'] = foo
msg.set_payload(obj)
cm = ContentManager()
cm.add_set_handler(key, foo_setter)
m = self._make_message()
msg_obj = self.Thing()
cm.set_content(m, msg_obj, foo='bar')
self.assertEqual(m['X-Foo-Header'], 'bar')
self.assertEqual(m.get_payload(), msg_obj)
def set_key_as_set_content_key_order(self, order, key):
def foo_setter(msg, obj):
msg['X-FooBar-Header'] = 'foo'
msg.set_payload(obj)
def bar_setter(msg, obj):
msg['X-FooBar-Header'] = 'bar'
cm = ContentManager()
cm.add_set_handler(key, foo_setter)
for precedence, key in self.get_key_params.values():
if precedence > order:
cm.add_set_handler(key, bar_setter)
m = self._make_message()
msg_obj = self.Thing()
cm.set_content(m, msg_obj)
self.assertEqual(m['X-FooBar-Header'], 'foo')
self.assertEqual(m.get_payload(), msg_obj)
def test_set_content_raises_if_unknown_type_and_no_default(self):
cm = ContentManager()
m = self._make_message()
msg_obj = self.Thing()
with self.assertRaisesRegex(KeyError, self.testobject_full_path):
cm.set_content(m, msg_obj)
def test_set_content_raises_if_called_on_multipart(self):
cm = ContentManager()
m = self._make_message()
m['Content-Type'] = 'multipart/foo'
with self.assertRaises(TypeError):
cm.set_content(m, 'test')
def test_set_content_calls_clear_content(self):
m = self._make_message()
m['Content-Foo'] = 'bar'
m['Content-Type'] = 'text/html'
m['To'] = 'test'
m.set_payload('abc')
cm = ContentManager()
cm.add_set_handler(str, lambda *args, **kw: None)
m.set_content('xyz', content_manager=cm)
self.assertIsNone(m['Content-Foo'])
self.assertIsNone(m['Content-Type'])
self.assertEqual(m['To'], 'test')
self.assertIsNone(m.get_payload())
@parameterize
class TestRawDataManager(TestEmailBase):
# Note: these tests are dependent on the order in which headers are added
# to the message objects by the code. There's no defined ordering in
# RFC5322/MIME, so this makes the tests more fragile than the standards
# require. However, if the header order changes it is best to understand
# *why*, and make sure it isn't a subtle bug in whatever change was
# applied.
policy = policy.default.clone(max_line_length=60,
content_manager=raw_data_manager)
message = EmailMessage
def test_get_text_plain(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: text/plain
Basic text.
"""))
self.assertEqual(raw_data_manager.get_content(m), "Basic text.\n")
def test_get_text_html(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: text/html
<p>Basic text.</p>
"""))
self.assertEqual(raw_data_manager.get_content(m),
"<p>Basic text.</p>\n")
def test_get_text_plain_latin1(self):
m = self._bytes_msg(textwrap.dedent("""\
Content-Type: text/plain; charset=latin1
Basìc tëxt.
""").encode('latin1'))
self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt.\n")
def test_get_text_plain_latin1_quoted_printable(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: text/plain; charset="latin-1"
Content-Transfer-Encoding: quoted-printable
Bas=ECc t=EBxt.
"""))
self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt.\n")
def test_get_text_plain_utf8_base64(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: text/plain; charset="utf8"
Content-Transfer-Encoding: base64
QmFzw6xjIHTDq3h0Lgo=
"""))
self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt.\n")
def test_get_text_plain_bad_utf8_quoted_printable(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: text/plain; charset="utf8"
Content-Transfer-Encoding: quoted-printable
Bas=c3=acc t=c3=abxt=fd.
"""))
self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt<78>.\n")
def test_get_text_plain_bad_utf8_quoted_printable_ignore_errors(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: text/plain; charset="utf8"
Content-Transfer-Encoding: quoted-printable
Bas=c3=acc t=c3=abxt=fd.
"""))
self.assertEqual(raw_data_manager.get_content(m, errors='ignore'),
"Basìc tëxt.\n")
def test_get_text_plain_utf8_base64_recoverable_bad_CTE_data(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: text/plain; charset="utf8"
Content-Transfer-Encoding: base64
QmFzw6xjIHTDq3h0Lgo\xFF=
"""))
self.assertEqual(raw_data_manager.get_content(m, errors='ignore'),
"Basìc tëxt.\n")
def test_get_text_invalid_keyword(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: text/plain
Basic text.
"""))
with self.assertRaises(TypeError):
raw_data_manager.get_content(m, foo='ignore')
def test_get_non_text(self):
template = textwrap.dedent("""\
Content-Type: {}
Content-Transfer-Encoding: base64
Ym9ndXMgZGF0YQ==
""")
for maintype in 'audio image video application'.split():
with self.subTest(maintype=maintype):
m = self._str_msg(template.format(maintype+'/foo'))
self.assertEqual(raw_data_manager.get_content(m), b"bogus data")
def test_get_non_text_invalid_keyword(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: image/jpg
Content-Transfer-Encoding: base64
Ym9ndXMgZGF0YQ==
"""))
with self.assertRaises(TypeError):
raw_data_manager.get_content(m, errors='ignore')
def test_get_raises_on_multipart(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: multipart/mixed; boundary="==="
--===
--===--
"""))
with self.assertRaises(KeyError):
raw_data_manager.get_content(m)
def test_get_message_rfc822_and_external_body(self):
template = textwrap.dedent("""\
Content-Type: message/{}
To: foo@example.com
From: bar@example.com
Subject: example
an example message
""")
for subtype in 'rfc822 external-body'.split():
with self.subTest(subtype=subtype):
m = self._str_msg(template.format(subtype))
sub_msg = raw_data_manager.get_content(m)
self.assertIsInstance(sub_msg, self.message)
self.assertEqual(raw_data_manager.get_content(sub_msg),
"an example message\n")
self.assertEqual(sub_msg['to'], 'foo@example.com')
self.assertEqual(sub_msg['from'].addresses[0].username, 'bar')
def test_get_message_non_rfc822_or_external_body_yields_bytes(self):
m = self._str_msg(textwrap.dedent("""\
Content-Type: message/partial
To: foo@example.com
From: bar@example.com
Subject: example
The real body is in another message.
"""))
self.assertEqual(raw_data_manager.get_content(m)[:10], b'To: foo@ex')
def test_set_text_plain(self):
m = self._make_message()
content = "Simple message.\n"
raw_data_manager.set_content(m, content)
self.assertEqual(str(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Simple message.
"""))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_html(self):
m = self._make_message()
content = "<p>Simple message.</p>\n"
raw_data_manager.set_content(m, content, subtype='html')
self.assertEqual(str(m), textwrap.dedent("""\
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: 7bit
<p>Simple message.</p>
"""))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_charset_latin_1(self):
m = self._make_message()
content = "Simple message.\n"
raw_data_manager.set_content(m, content, charset='latin-1')
self.assertEqual(str(m), textwrap.dedent("""\
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Simple message.
"""))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_short_line_minimal_non_ascii_heuristics(self):
m = self._make_message()
content = "et là il est monté sur moi et il commence à m'éto.\n"
raw_data_manager.set_content(m, content)
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
et il est monté sur moi et il commence à m'éto.
""").encode('utf-8'))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_long_line_minimal_non_ascii_heuristics(self):
m = self._make_message()
content = ("j'ai un problème de python. il est sorti de son"
" vivarium. et là il est monté sur moi et il commence"
" à m'éto.\n")
raw_data_manager.set_content(m, content)
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
j'ai un probl=C3=A8me de python. il est sorti de son vivari=
um. et l=C3=A0 il est mont=C3=A9 sur moi et il commence =
=C3=A0 m'=C3=A9to.
""").encode('utf-8'))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_11_lines_long_line_minimal_non_ascii_heuristics(self):
m = self._make_message()
content = '\n'*10 + (
"j'ai un problème de python. il est sorti de son"
" vivarium. et là il est monté sur moi et il commence"
" à m'éto.\n")
raw_data_manager.set_content(m, content)
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
""" + '\n'*10 + """
j'ai un probl=C3=A8me de python. il est sorti de son vivari=
um. et l=C3=A0 il est mont=C3=A9 sur moi et il commence =
=C3=A0 m'=C3=A9to.
""").encode('utf-8'))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_maximal_non_ascii_heuristics(self):
m = self._make_message()
content = "áàäéèęöő.\n"
raw_data_manager.set_content(m, content)
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
áàäéèęöő.
""").encode('utf-8'))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_11_lines_maximal_non_ascii_heuristics(self):
m = self._make_message()
content = '\n'*10 + "áàäéèęöő.\n"
raw_data_manager.set_content(m, content)
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
""" + '\n'*10 + """
áàäéèęöő.
""").encode('utf-8'))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_long_line_maximal_non_ascii_heuristics(self):
m = self._make_message()
content = ("áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő"
"áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő"
"áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő.\n")
raw_data_manager.set_content(m, content)
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
w6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOgw6TDqcOoxJnD
tsWRw6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOgw6TDqcOo
xJnDtsWRw6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOgw6TD
qcOoxJnDtsWRw6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOg
w6TDqcOoxJnDtsWRLgo=
""").encode('utf-8'))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_11_lines_long_line_maximal_non_ascii_heuristics(self):
# Yes, it chooses "wrong" here. It's a heuristic. So this result
# could change if we come up with a better heuristic.
m = self._make_message()
content = ('\n'*10 +
"áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő"
"áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő"
"áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő.\n")
raw_data_manager.set_content(m, "\n"*10 +
"áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő"
"áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő"
"áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő.\n")
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
""" + '\n'*10 + """
=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=
=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=
=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=
=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=
=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=
=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=
=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=
=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=
=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=
=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=
=C5=91.
""").encode('utf-8'))
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
def test_set_text_non_ascii_with_cte_7bit_raises(self):
m = self._make_message()
with self.assertRaises(UnicodeError):
raw_data_manager.set_content(m,"áàäéèęöő.\n", cte='7bit')
def test_set_text_non_ascii_with_charset_ascii_raises(self):
m = self._make_message()
with self.assertRaises(UnicodeError):
raw_data_manager.set_content(m,"áàäéèęöő.\n", charset='ascii')
def test_set_text_non_ascii_with_cte_7bit_and_charset_ascii_raises(self):
m = self._make_message()
with self.assertRaises(UnicodeError):
raw_data_manager.set_content(m,"áàäéèęöő.\n", cte='7bit', charset='ascii')
def test_set_message(self):
m = self._make_message()
m['Subject'] = "Forwarded message"
content = self._make_message()
content['To'] = 'python@vivarium.org'
content['From'] = 'police@monty.org'
content['Subject'] = "get back in your box"
content.set_content("Or face the comfy chair.")
raw_data_manager.set_content(m, content)
self.assertEqual(str(m), textwrap.dedent("""\
Subject: Forwarded message
Content-Type: message/rfc822
Content-Transfer-Encoding: 8bit
To: python@vivarium.org
From: police@monty.org
Subject: get back in your box
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0
Or face the comfy chair.
"""))
payload = m.get_payload(0)
self.assertIsInstance(payload, self.message)
self.assertEqual(str(payload), str(content))
self.assertIsInstance(m.get_content(), self.message)
self.assertEqual(str(m.get_content()), str(content))
def test_set_message_with_non_ascii_and_coercion_to_7bit(self):
m = self._make_message()
m['Subject'] = "Escape report"
content = self._make_message()
content['To'] = 'police@monty.org'
content['From'] = 'victim@monty.org'
content['Subject'] = "Help"
content.set_content("j'ai un problème de python. il est sorti de son"
" vivarium.")
raw_data_manager.set_content(m, content)
self.assertEqual(bytes(m), textwrap.dedent("""\
Subject: Escape report
Content-Type: message/rfc822
Content-Transfer-Encoding: 8bit
To: police@monty.org
From: victim@monty.org
Subject: Help
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0
j'ai un problème de python. il est sorti de son vivarium.
""").encode('utf-8'))
# The choice of base64 for the body encoding is because generator
# doesn't bother with heuristics and uses it unconditionally for utf-8
# text.
# XXX: the first cte should be 7bit, too...that's a generator bug.
# XXX: the line length in the body also looks like a generator bug.
self.assertEqual(m.as_string(maxheaderlen=self.policy.max_line_length),
textwrap.dedent("""\
Subject: Escape report
Content-Type: message/rfc822
Content-Transfer-Encoding: 8bit
To: police@monty.org
From: victim@monty.org
Subject: Help
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
MIME-Version: 1.0
aidhaSB1biBwcm9ibMOobWUgZGUgcHl0aG9uLiBpbCBlc3Qgc29ydGkgZGUgc29uIHZpdmFyaXVt
Lgo=
"""))
self.assertIsInstance(m.get_content(), self.message)
self.assertEqual(str(m.get_content()), str(content))
def test_set_message_invalid_cte_raises(self):
m = self._make_message()
content = self._make_message()
for cte in 'quoted-printable base64'.split():
for subtype in 'rfc822 external-body'.split():
with self.subTest(cte=cte, subtype=subtype):
with self.assertRaises(ValueError) as ar:
m.set_content(content, subtype, cte=cte)
exc = str(ar.exception)
self.assertIn(cte, exc)
self.assertIn(subtype, exc)
subtype = 'external-body'
for cte in '8bit binary'.split():
with self.subTest(cte=cte, subtype=subtype):
with self.assertRaises(ValueError) as ar:
m.set_content(content, subtype, cte=cte)
exc = str(ar.exception)
self.assertIn(cte, exc)
self.assertIn(subtype, exc)
def test_set_image_jpg(self):
for content in (b"bogus content",
bytearray(b"bogus content"),
memoryview(b"bogus content")):
with self.subTest(content=content):
m = self._make_message()
raw_data_manager.set_content(m, content, 'image', 'jpeg')
self.assertEqual(str(m), textwrap.dedent("""\
Content-Type: image/jpeg
Content-Transfer-Encoding: base64
Ym9ndXMgY29udGVudA==
"""))
self.assertEqual(m.get_payload(decode=True), content)
self.assertEqual(m.get_content(), content)
def test_set_audio_aif_with_quoted_printable_cte(self):
# Why you would use qp, I don't know, but it is technically supported.
# XXX: the incorrect line length is because binascii.b2a_qp doesn't
# support a line length parameter, but we must use it to get newline
# encoding.
# XXX: what about that lack of tailing newline? Do we actually handle
# that correctly in all cases? That is, if the *source* has an
# unencoded newline, do we add an extra newline to the returned payload
# or not? And can that actually be disambiguated based on the RFC?
m = self._make_message()
content = b'b\xFFgus\tcon\nt\rent ' + b'z'*100
m.set_content(content, 'audio', 'aif', cte='quoted-printable')
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: audio/aif
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0
b=FFgus=09con=0At=0Dent=20zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz""").encode('latin-1'))
self.assertEqual(m.get_payload(decode=True), content)
self.assertEqual(m.get_content(), content)
def test_set_video_mpeg_with_binary_cte(self):
m = self._make_message()
content = b'b\xFFgus\tcon\nt\rent ' + b'z'*100
m.set_content(content, 'video', 'mpeg', cte='binary')
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: video/mpeg
Content-Transfer-Encoding: binary
MIME-Version: 1.0
""").encode('ascii') +
# XXX: the second \n ought to be a \r, but generator gets it wrong.
# THIS MEANS WE DON'T ACTUALLY SUPPORT THE 'binary' CTE.
b'b\xFFgus\tcon\nt\nent zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' +
b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz')
self.assertEqual(m.get_payload(decode=True), content)
self.assertEqual(m.get_content(), content)
def test_set_application_octet_stream_with_8bit_cte(self):
# In 8bit mode, universal line end logic applies. It is up to the
# application to make sure the lines are short enough; we don't check.
m = self._make_message()
content = b'b\xFFgus\tcon\nt\rent\n' + b'z'*60 + b'\n'
m.set_content(content, 'application', 'octet-stream', cte='8bit')
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: application/octet-stream
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0
""").encode('ascii') +
b'b\xFFgus\tcon\nt\nent\n' +
b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n')
self.assertEqual(m.get_payload(decode=True), content)
self.assertEqual(m.get_content(), content)
def test_set_headers_from_header_objects(self):
m = self._make_message()
content = "Simple message.\n"
header_factory = self.policy.header_factory
raw_data_manager.set_content(m, content, headers=(
header_factory("To", "foo@example.com"),
header_factory("From", "foo@example.com"),
header_factory("Subject", "I'm talking to myself.")))
self.assertEqual(str(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
To: foo@example.com
From: foo@example.com
Subject: I'm talking to myself.
Content-Transfer-Encoding: 7bit
Simple message.
"""))
def test_set_headers_from_strings(self):
m = self._make_message()
content = "Simple message.\n"
raw_data_manager.set_content(m, content, headers=(
"X-Foo-Header: foo",
"X-Bar-Header: bar",))
self.assertEqual(str(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
X-Foo-Header: foo
X-Bar-Header: bar
Content-Transfer-Encoding: 7bit
Simple message.
"""))
def test_set_headers_with_invalid_duplicate_string_header_raises(self):
m = self._make_message()
content = "Simple message.\n"
with self.assertRaisesRegex(ValueError, 'Content-Type'):
raw_data_manager.set_content(m, content, headers=(
"Content-Type: foo/bar",)
)
def test_set_headers_with_invalid_duplicate_header_header_raises(self):
m = self._make_message()
content = "Simple message.\n"
header_factory = self.policy.header_factory
with self.assertRaisesRegex(ValueError, 'Content-Type'):
raw_data_manager.set_content(m, content, headers=(
header_factory("Content-Type", " foo/bar"),)
)
def test_set_headers_with_defective_string_header_raises(self):
m = self._make_message()
content = "Simple message.\n"
with self.assertRaisesRegex(ValueError, 'a@fairly@@invalid@address'):
raw_data_manager.set_content(m, content, headers=(
'To: a@fairly@@invalid@address',)
)
print(m['To'].defects)
def test_set_headers_with_defective_header_header_raises(self):
m = self._make_message()
content = "Simple message.\n"
header_factory = self.policy.header_factory
with self.assertRaisesRegex(ValueError, 'a@fairly@@invalid@address'):
raw_data_manager.set_content(m, content, headers=(
header_factory('To', 'a@fairly@@invalid@address'),)
)
print(m['To'].defects)
def test_set_disposition_inline(self):
m = self._make_message()
m.set_content('foo', disposition='inline')
self.assertEqual(m['Content-Disposition'], 'inline')
def test_set_disposition_attachment(self):
m = self._make_message()
m.set_content('foo', disposition='attachment')
self.assertEqual(m['Content-Disposition'], 'attachment')
def test_set_disposition_foo(self):
m = self._make_message()
m.set_content('foo', disposition='foo')
self.assertEqual(m['Content-Disposition'], 'foo')
# XXX: we should have a 'strict' policy mode (beyond raise_on_defect) that
# would cause 'foo' above to raise.
def test_set_filename(self):
m = self._make_message()
m.set_content('foo', filename='bar.txt')
self.assertEqual(m['Content-Disposition'],
'attachment; filename="bar.txt"')
def test_set_filename_and_disposition_inline(self):
m = self._make_message()
m.set_content('foo', disposition='inline', filename='bar.txt')
self.assertEqual(m['Content-Disposition'], 'inline; filename="bar.txt"')
def test_set_non_ascii_filename(self):
m = self._make_message()
m.set_content('foo', filename='ábárî.txt')
self.assertEqual(bytes(m), textwrap.dedent("""\
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename*=utf-8''%C3%A1b%C3%A1r%C3%AE.txt
MIME-Version: 1.0
foo
""").encode('ascii'))
content_object_params = {
'text_plain': ('content', ()),
'text_html': ('content', ('html',)),
'application_octet_stream': (b'content',
('application', 'octet_stream')),
'image_jpeg': (b'content', ('image', 'jpeg')),
'message_rfc822': (message(), ()),
'message_external_body': (message(), ('external-body',)),
}
def content_object_as_header_receiver(self, obj, mimetype):
m = self._make_message()
m.set_content(obj, *mimetype, headers=(
'To: foo@example.com',
'From: bar@simple.net'))
self.assertEqual(m['to'], 'foo@example.com')
self.assertEqual(m['from'], 'bar@simple.net')
def content_object_as_disposition_inline_receiver(self, obj, mimetype):
m = self._make_message()
m.set_content(obj, *mimetype, disposition='inline')
self.assertEqual(m['Content-Disposition'], 'inline')
def content_object_as_non_ascii_filename_receiver(self, obj, mimetype):
m = self._make_message()
m.set_content(obj, *mimetype, disposition='inline', filename='bár.txt')
self.assertEqual(m['Content-Disposition'], 'inline; filename="bár.txt"')
self.assertEqual(m.get_filename(), "bár.txt")
self.assertEqual(m['Content-Disposition'].params['filename'], "bár.txt")
def content_object_as_cid_receiver(self, obj, mimetype):
m = self._make_message()
m.set_content(obj, *mimetype, cid='some_random_stuff')
self.assertEqual(m['Content-ID'], 'some_random_stuff')
def content_object_as_params_receiver(self, obj, mimetype):
m = self._make_message()
params = {'foo': 'bár', 'abc': 'xyz'}
m.set_content(obj, *mimetype, params=params)
if isinstance(obj, str):
params['charset'] = 'utf-8'
self.assertEqual(m['Content-Type'].params, params)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,337 @@
import textwrap
import unittest
import contextlib
from email import policy
from email import errors
from test.test_email import TestEmailBase
class TestDefectsBase:
policy = policy.default
raise_expected = False
@contextlib.contextmanager
def _raise_point(self, defect):
yield
def test_same_boundary_inner_outer(self):
source = textwrap.dedent("""\
Subject: XX
From: xx@xx.dk
To: XX
Mime-version: 1.0
Content-type: multipart/mixed;
boundary="MS_Mac_OE_3071477847_720252_MIME_Part"
--MS_Mac_OE_3071477847_720252_MIME_Part
Content-type: multipart/alternative;
boundary="MS_Mac_OE_3071477847_720252_MIME_Part"
--MS_Mac_OE_3071477847_720252_MIME_Part
Content-type: text/plain; charset="ISO-8859-1"
Content-transfer-encoding: quoted-printable
text
--MS_Mac_OE_3071477847_720252_MIME_Part
Content-type: text/html; charset="ISO-8859-1"
Content-transfer-encoding: quoted-printable
<HTML></HTML>
--MS_Mac_OE_3071477847_720252_MIME_Part--
--MS_Mac_OE_3071477847_720252_MIME_Part
Content-type: image/gif; name="xx.gif";
Content-disposition: attachment
Content-transfer-encoding: base64
Some removed base64 encoded chars.
--MS_Mac_OE_3071477847_720252_MIME_Part--
""")
# XXX better would be to actually detect the duplicate.
with self._raise_point(errors.StartBoundaryNotFoundDefect):
msg = self._str_msg(source)
if self.raise_expected: return
inner = msg.get_payload(0)
self.assertTrue(hasattr(inner, 'defects'))
self.assertEqual(len(self.get_defects(inner)), 1)
self.assertIsInstance(self.get_defects(inner)[0],
errors.StartBoundaryNotFoundDefect)
def test_multipart_no_boundary(self):
source = textwrap.dedent("""\
Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800)
From: foobar
Subject: broken mail
MIME-Version: 1.0
Content-Type: multipart/report; report-type=delivery-status;
--JAB03225.986577786/zinfandel.lacita.com
One part
--JAB03225.986577786/zinfandel.lacita.com
Content-Type: message/delivery-status
Header: Another part
--JAB03225.986577786/zinfandel.lacita.com--
""")
with self._raise_point(errors.NoBoundaryInMultipartDefect):
msg = self._str_msg(source)
if self.raise_expected: return
self.assertIsInstance(msg.get_payload(), str)
self.assertEqual(len(self.get_defects(msg)), 2)
self.assertIsInstance(self.get_defects(msg)[0],
errors.NoBoundaryInMultipartDefect)
self.assertIsInstance(self.get_defects(msg)[1],
errors.MultipartInvariantViolationDefect)
multipart_msg = textwrap.dedent("""\
Date: Wed, 14 Nov 2007 12:56:23 GMT
From: foo@bar.invalid
To: foo@bar.invalid
Subject: Content-Transfer-Encoding: base64 and multipart
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="===============3344438784458119861=="{}
--===============3344438784458119861==
Content-Type: text/plain
Test message
--===============3344438784458119861==
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
YWJj
--===============3344438784458119861==--
""")
def test_multipart_invalid_cte(self):
with self._raise_point(
errors.InvalidMultipartContentTransferEncodingDefect):
msg = self._str_msg(
self.multipart_msg.format(
"\nContent-Transfer-Encoding: base64"))
if self.raise_expected: return
self.assertEqual(len(self.get_defects(msg)), 1)
self.assertIsInstance(self.get_defects(msg)[0],
errors.InvalidMultipartContentTransferEncodingDefect)
def test_multipart_no_cte_no_defect(self):
if self.raise_expected: return
msg = self._str_msg(self.multipart_msg.format(''))
self.assertEqual(len(self.get_defects(msg)), 0)
def test_multipart_valid_cte_no_defect(self):
if self.raise_expected: return
for cte in ('7bit', '8bit', 'BINary'):
msg = self._str_msg(
self.multipart_msg.format("\nContent-Transfer-Encoding: "+cte))
self.assertEqual(len(self.get_defects(msg)), 0, "cte="+cte)
def test_lying_multipart(self):
source = textwrap.dedent("""\
From: "Allison Dunlap" <xxx@example.com>
To: yyy@example.com
Subject: 64423
Date: Sun, 11 Jul 2004 16:09:27 -0300
MIME-Version: 1.0
Content-Type: multipart/alternative;
Blah blah blah
""")
with self._raise_point(errors.NoBoundaryInMultipartDefect):
msg = self._str_msg(source)
if self.raise_expected: return
self.assertTrue(hasattr(msg, 'defects'))
self.assertEqual(len(self.get_defects(msg)), 2)
self.assertIsInstance(self.get_defects(msg)[0],
errors.NoBoundaryInMultipartDefect)
self.assertIsInstance(self.get_defects(msg)[1],
errors.MultipartInvariantViolationDefect)
def test_missing_start_boundary(self):
source = textwrap.dedent("""\
Content-Type: multipart/mixed; boundary="AAA"
From: Mail Delivery Subsystem <xxx@example.com>
To: yyy@example.com
--AAA
Stuff
--AAA
Content-Type: message/rfc822
From: webmaster@python.org
To: zzz@example.com
Content-Type: multipart/mixed; boundary="BBB"
--BBB--
--AAA--
""")
# The message structure is:
#
# multipart/mixed
# text/plain
# message/rfc822
# multipart/mixed [*]
#
# [*] This message is missing its start boundary
with self._raise_point(errors.StartBoundaryNotFoundDefect):
outer = self._str_msg(source)
if self.raise_expected: return
bad = outer.get_payload(1).get_payload(0)
self.assertEqual(len(self.get_defects(bad)), 1)
self.assertIsInstance(self.get_defects(bad)[0],
errors.StartBoundaryNotFoundDefect)
def test_first_line_is_continuation_header(self):
with self._raise_point(errors.FirstHeaderLineIsContinuationDefect):
msg = self._str_msg(' Line 1\nSubject: test\n\nbody')
if self.raise_expected: return
self.assertEqual(msg.keys(), ['Subject'])
self.assertEqual(msg.get_payload(), 'body')
self.assertEqual(len(self.get_defects(msg)), 1)
self.assertDefectsEqual(self.get_defects(msg),
[errors.FirstHeaderLineIsContinuationDefect])
self.assertEqual(self.get_defects(msg)[0].line, ' Line 1\n')
def test_missing_header_body_separator(self):
# Our heuristic if we see a line that doesn't look like a header (no
# leading whitespace but no ':') is to assume that the blank line that
# separates the header from the body is missing, and to stop parsing
# headers and start parsing the body.
with self._raise_point(errors.MissingHeaderBodySeparatorDefect):
msg = self._str_msg('Subject: test\nnot a header\nTo: abc\n\nb\n')
if self.raise_expected: return
self.assertEqual(msg.keys(), ['Subject'])
self.assertEqual(msg.get_payload(), 'not a header\nTo: abc\n\nb\n')
self.assertDefectsEqual(self.get_defects(msg),
[errors.MissingHeaderBodySeparatorDefect])
def test_bad_padding_in_base64_payload(self):
source = textwrap.dedent("""\
Subject: test
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
dmk
""")
msg = self._str_msg(source)
with self._raise_point(errors.InvalidBase64PaddingDefect):
payload = msg.get_payload(decode=True)
if self.raise_expected: return
self.assertEqual(payload, b'vi')
self.assertDefectsEqual(self.get_defects(msg),
[errors.InvalidBase64PaddingDefect])
def test_invalid_chars_in_base64_payload(self):
source = textwrap.dedent("""\
Subject: test
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
dm\x01k===
""")
msg = self._str_msg(source)
with self._raise_point(errors.InvalidBase64CharactersDefect):
payload = msg.get_payload(decode=True)
if self.raise_expected: return
self.assertEqual(payload, b'vi')
self.assertDefectsEqual(self.get_defects(msg),
[errors.InvalidBase64CharactersDefect])
def test_invalid_length_of_base64_payload(self):
source = textwrap.dedent("""\
Subject: test
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
abcde
""")
msg = self._str_msg(source)
with self._raise_point(errors.InvalidBase64LengthDefect):
payload = msg.get_payload(decode=True)
if self.raise_expected: return
self.assertEqual(payload, b'abcde')
self.assertDefectsEqual(self.get_defects(msg),
[errors.InvalidBase64LengthDefect])
def test_missing_ending_boundary(self):
source = textwrap.dedent("""\
To: 1@harrydomain4.com
Subject: Fwd: 1
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="------------000101020201080900040301"
--------------000101020201080900040301
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Alternative 1
--------------000101020201080900040301
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Alternative 2
""")
with self._raise_point(errors.CloseBoundaryNotFoundDefect):
msg = self._str_msg(source)
if self.raise_expected: return
self.assertEqual(len(msg.get_payload()), 2)
self.assertEqual(msg.get_payload(1).get_payload(), 'Alternative 2\n')
self.assertDefectsEqual(self.get_defects(msg),
[errors.CloseBoundaryNotFoundDefect])
class TestDefectDetection(TestDefectsBase, TestEmailBase):
def get_defects(self, obj):
return obj.defects
class TestDefectCapture(TestDefectsBase, TestEmailBase):
class CapturePolicy(policy.EmailPolicy):
captured = None
def register_defect(self, obj, defect):
self.captured.append(defect)
def setUp(self):
self.policy = self.CapturePolicy(captured=list())
def get_defects(self, obj):
return self.policy.captured
class TestDefectRaising(TestDefectsBase, TestEmailBase):
policy = TestDefectsBase.policy
policy = policy.clone(raise_on_defect=True)
raise_expected = True
@contextlib.contextmanager
def _raise_point(self, defect):
with self.assertRaises(defect):
yield
if __name__ == '__main__':
unittest.main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,296 @@
import io
import textwrap
import unittest
from email import message_from_string, message_from_bytes
from email.message import EmailMessage
from email.generator import Generator, BytesGenerator
from email import policy
from test.test_email import TestEmailBase, parameterize
@parameterize
class TestGeneratorBase:
policy = policy.default
def msgmaker(self, msg, policy=None):
policy = self.policy if policy is None else policy
return self.msgfunc(msg, policy=policy)
refold_long_expected = {
0: textwrap.dedent("""\
To: whom_it_may_concern@example.com
From: nobody_you_want_to_know@example.com
Subject: We the willing led by the unknowing are doing the
impossible for the ungrateful. We have done so much for so long with so little
we are now qualified to do anything with nothing.
None
"""),
40: textwrap.dedent("""\
To: whom_it_may_concern@example.com
From:
nobody_you_want_to_know@example.com
Subject: We the willing led by the
unknowing are doing the impossible for
the ungrateful. We have done so much
for so long with so little we are now
qualified to do anything with nothing.
None
"""),
20: textwrap.dedent("""\
To:
whom_it_may_concern@example.com
From:
nobody_you_want_to_know@example.com
Subject: We the
willing led by the
unknowing are doing
the impossible for
the ungrateful. We
have done so much
for so long with so
little we are now
qualified to do
anything with
nothing.
None
"""),
}
refold_long_expected[100] = refold_long_expected[0]
refold_all_expected = refold_long_expected.copy()
refold_all_expected[0] = (
"To: whom_it_may_concern@example.com\n"
"From: nobody_you_want_to_know@example.com\n"
"Subject: We the willing led by the unknowing are doing the "
"impossible for the ungrateful. We have done so much for "
"so long with so little we are now qualified to do anything "
"with nothing.\n"
"\n"
"None\n")
refold_all_expected[100] = (
"To: whom_it_may_concern@example.com\n"
"From: nobody_you_want_to_know@example.com\n"
"Subject: We the willing led by the unknowing are doing the "
"impossible for the ungrateful. We have\n"
" done so much for so long with so little we are now qualified "
"to do anything with nothing.\n"
"\n"
"None\n")
length_params = [n for n in refold_long_expected]
def length_as_maxheaderlen_parameter(self, n):
msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
s = self.ioclass()
g = self.genclass(s, maxheaderlen=n, policy=self.policy)
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n]))
def length_as_max_line_length_policy(self, n):
msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
s = self.ioclass()
g = self.genclass(s, policy=self.policy.clone(max_line_length=n))
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n]))
def length_as_maxheaderlen_parm_overrides_policy(self, n):
msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
s = self.ioclass()
g = self.genclass(s, maxheaderlen=n,
policy=self.policy.clone(max_line_length=10))
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n]))
def length_as_max_line_length_with_refold_none_does_not_fold(self, n):
msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
s = self.ioclass()
g = self.genclass(s, policy=self.policy.clone(refold_source='none',
max_line_length=n))
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[0]))
def length_as_max_line_length_with_refold_all_folds(self, n):
msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
s = self.ioclass()
g = self.genclass(s, policy=self.policy.clone(refold_source='all',
max_line_length=n))
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(self.refold_all_expected[n]))
def test_crlf_control_via_policy(self):
source = "Subject: test\r\n\r\ntest body\r\n"
expected = source
msg = self.msgmaker(self.typ(source))
s = self.ioclass()
g = self.genclass(s, policy=policy.SMTP)
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(expected))
def test_flatten_linesep_overrides_policy(self):
source = "Subject: test\n\ntest body\n"
expected = source
msg = self.msgmaker(self.typ(source))
s = self.ioclass()
g = self.genclass(s, policy=policy.SMTP)
g.flatten(msg, linesep='\n')
self.assertEqual(s.getvalue(), self.typ(expected))
def test_set_mangle_from_via_policy(self):
source = textwrap.dedent("""\
Subject: test that
from is mangled in the body!
From time to time I write a rhyme.
""")
variants = (
(None, True),
(policy.compat32, True),
(policy.default, False),
(policy.default.clone(mangle_from_=True), True),
)
for p, mangle in variants:
expected = source.replace('From ', '>From ') if mangle else source
with self.subTest(policy=p, mangle_from_=mangle):
msg = self.msgmaker(self.typ(source))
s = self.ioclass()
g = self.genclass(s, policy=p)
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(expected))
def test_compat32_max_line_length_does_not_fold_when_none(self):
msg = self.msgmaker(self.typ(self.refold_long_expected[0]))
s = self.ioclass()
g = self.genclass(s, policy=policy.compat32.clone(max_line_length=None))
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[0]))
def test_rfc2231_wrapping(self):
# This is pretty much just to make sure we don't have an infinite
# loop; I don't expect anyone to hit this in the field.
msg = self.msgmaker(self.typ(textwrap.dedent("""\
To: nobody
Content-Disposition: attachment;
filename="afilenamelongenoghtowraphere"
None
""")))
expected = textwrap.dedent("""\
To: nobody
Content-Disposition: attachment;
filename*0*=us-ascii''afilename;
filename*1*=longenoghtowraphere
None
""")
s = self.ioclass()
g = self.genclass(s, policy=self.policy.clone(max_line_length=33))
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(expected))
def test_rfc2231_wrapping_switches_to_default_len_if_too_narrow(self):
# This is just to make sure we don't have an infinite loop; I don't
# expect anyone to hit this in the field, so I'm not bothering to make
# the result optimal (the encoding isn't needed).
msg = self.msgmaker(self.typ(textwrap.dedent("""\
To: nobody
Content-Disposition: attachment;
filename="afilenamelongenoghtowraphere"
None
""")))
expected = textwrap.dedent("""\
To: nobody
Content-Disposition:
attachment;
filename*0*=us-ascii''afilenamelongenoghtowraphere
None
""")
s = self.ioclass()
g = self.genclass(s, policy=self.policy.clone(max_line_length=20))
g.flatten(msg)
self.assertEqual(s.getvalue(), self.typ(expected))
class TestGenerator(TestGeneratorBase, TestEmailBase):
msgfunc = staticmethod(message_from_string)
genclass = Generator
ioclass = io.StringIO
typ = str
class TestBytesGenerator(TestGeneratorBase, TestEmailBase):
msgfunc = staticmethod(message_from_bytes)
genclass = BytesGenerator
ioclass = io.BytesIO
typ = lambda self, x: x.encode('ascii')
def test_cte_type_7bit_handles_unknown_8bit(self):
source = ("Subject: Maintenant je vous présente mon "
"collègue\n\n").encode('utf-8')
expected = ('Subject: Maintenant je vous =?unknown-8bit?q?'
'pr=C3=A9sente_mon_coll=C3=A8gue?=\n\n').encode('ascii')
msg = message_from_bytes(source)
s = io.BytesIO()
g = BytesGenerator(s, policy=self.policy.clone(cte_type='7bit'))
g.flatten(msg)
self.assertEqual(s.getvalue(), expected)
def test_cte_type_7bit_transforms_8bit_cte(self):
source = textwrap.dedent("""\
From: foo@bar.com
To: Dinsdale
Subject: Nudge nudge, wink, wink
Mime-Version: 1.0
Content-Type: text/plain; charset="latin-1"
Content-Transfer-Encoding: 8bit
oh , know what I mean, know what I mean?
""").encode('latin1')
msg = message_from_bytes(source)
expected = textwrap.dedent("""\
From: foo@bar.com
To: Dinsdale
Subject: Nudge nudge, wink, wink
Mime-Version: 1.0
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
oh l=E0 l=E0, know what I mean, know what I mean?
""").encode('ascii')
s = io.BytesIO()
g = BytesGenerator(s, policy=self.policy.clone(cte_type='7bit',
linesep='\n'))
g.flatten(msg)
self.assertEqual(s.getvalue(), expected)
def test_smtputf8_policy(self):
msg = EmailMessage()
msg['From'] = "Páolo <főo@bar.com>"
msg['To'] = 'Dinsdale'
msg['Subject'] = 'Nudge nudge, wink, wink \u1F609'
msg.set_content("oh là là, know what I mean, know what I mean?")
expected = textwrap.dedent("""\
From: Páolo <főo@bar.com>
To: Dinsdale
Subject: Nudge nudge, wink, wink \u1F609
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0
oh , know what I mean, know what I mean?
""").encode('utf-8').replace(b'\n', b'\r\n')
s = io.BytesIO()
g = BytesGenerator(s, policy=policy.SMTPUTF8)
g.flatten(msg)
self.assertEqual(s.getvalue(), expected)
if __name__ == '__main__':
unittest.main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,70 @@
"""Test the parser and generator are inverses.
Note that this is only strictly true if we are parsing RFC valid messages and
producing RFC valid messages.
"""
import io
import unittest
from email import policy, message_from_bytes
from email.message import EmailMessage
from email.generator import BytesGenerator
from test.test_email import TestEmailBase, parameterize
# This is like textwrap.dedent for bytes, except that it uses \r\n for the line
# separators on the rebuilt string.
def dedent(bstr):
lines = bstr.splitlines()
if not lines[0].strip():
raise ValueError("First line must contain text")
stripamt = len(lines[0]) - len(lines[0].lstrip())
return b'\r\n'.join(
[x[stripamt:] if len(x)>=stripamt else b''
for x in lines])
@parameterize
class TestInversion(TestEmailBase):
policy = policy.default
message = EmailMessage
def msg_as_input(self, msg):
m = message_from_bytes(msg, policy=policy.SMTP)
b = io.BytesIO()
g = BytesGenerator(b)
g.flatten(m)
self.assertEqual(b.getvalue(), msg)
# XXX: spaces are not preserved correctly here yet in the general case.
msg_params = {
'header_with_one_space_body': (dedent(b"""\
From: abc@xyz.com
X-Status:\x20
Subject: test
foo
"""),),
}
payload_params = {
'plain_text': dict(payload='This is a test\n'*20),
'base64_text': dict(payload=(('xy a'*40+'\n')*5), cte='base64'),
'qp_text': dict(payload=(('xy a'*40+'\n')*5), cte='quoted-printable'),
}
def payload_as_body(self, payload, **kw):
msg = self._make_message()
msg['From'] = 'foo'
msg['To'] = 'bar'
msg['Subject'] = 'payload round trip test'
msg.set_content(payload, **kw)
b = bytes(msg)
msg2 = message_from_bytes(b, policy=self.policy)
self.assertEqual(bytes(msg2), b)
self.assertEqual(msg2.get_content(), payload)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,803 @@
import unittest
import textwrap
from email import policy, message_from_string
from email.message import EmailMessage, MIMEPart
from test.test_email import TestEmailBase, parameterize
# Helper.
def first(iterable):
return next(filter(lambda x: x is not None, iterable), None)
class Test(TestEmailBase):
policy = policy.default
def test_error_on_setitem_if_max_count_exceeded(self):
m = self._str_msg("")
m['To'] = 'abc@xyz'
with self.assertRaises(ValueError):
m['To'] = 'xyz@abc'
def test_rfc2043_auto_decoded_and_emailmessage_used(self):
m = message_from_string(textwrap.dedent("""\
Subject: Ayons asperges pour le =?utf-8?q?d=C3=A9jeuner?=
From: =?utf-8?q?Pep=C3=A9?= Le Pew <pepe@example.com>
To: "Penelope Pussycat" <"penelope@example.com">
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
sample text
"""), policy=policy.default)
self.assertEqual(m['subject'], "Ayons asperges pour le déjeuner")
self.assertEqual(m['from'], "Pepé Le Pew <pepe@example.com>")
self.assertIsInstance(m, EmailMessage)
@parameterize
class TestEmailMessageBase:
policy = policy.default
# The first argument is a triple (related, html, plain) of indices into the
# list returned by 'walk' called on a Message constructed from the third.
# The indices indicate which part should match the corresponding part-type
# when passed to get_body (ie: the "first" part of that type in the
# message). The second argument is a list of indices into the 'walk' list
# of the attachments that should be returned by a call to
# 'iter_attachments'. The third argument is a list of indices into 'walk'
# that should be returned by a call to 'iter_parts'. Note that the first
# item returned by 'walk' is the Message itself.
message_params = {
'empty_message': (
(None, None, 0),
(),
(),
""),
'non_mime_plain': (
(None, None, 0),
(),
(),
textwrap.dedent("""\
To: foo@example.com
simple text body
""")),
'mime_non_text': (
(None, None, None),
(),
(),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: image/jpg
bogus body.
""")),
'plain_html_alternative': (
(None, 2, 1),
(),
(1, 2),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="==="
preamble
--===
Content-Type: text/plain
simple body
--===
Content-Type: text/html
<p>simple body</p>
--===--
""")),
'plain_html_mixed': (
(None, 2, 1),
(),
(1, 2),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==="
preamble
--===
Content-Type: text/plain
simple body
--===
Content-Type: text/html
<p>simple body</p>
--===--
""")),
'plain_html_attachment_mixed': (
(None, None, 1),
(2,),
(1, 2),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==="
--===
Content-Type: text/plain
simple body
--===
Content-Type: text/html
Content-Disposition: attachment
<p>simple body</p>
--===--
""")),
'html_text_attachment_mixed': (
(None, 2, None),
(1,),
(1, 2),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==="
--===
Content-Type: text/plain
Content-Disposition: AtTaChment
simple body
--===
Content-Type: text/html
<p>simple body</p>
--===--
""")),
'html_text_attachment_inline_mixed': (
(None, 2, 1),
(),
(1, 2),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==="
--===
Content-Type: text/plain
Content-Disposition: InLine
simple body
--===
Content-Type: text/html
Content-Disposition: inline
<p>simple body</p>
--===--
""")),
# RFC 2387
'related': (
(0, 1, None),
(2,),
(1, 2),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/related; boundary="==="; type=text/html
--===
Content-Type: text/html
<p>simple body</p>
--===
Content-Type: image/jpg
Content-ID: <image1>
bogus data
--===--
""")),
# This message structure will probably never be seen in the wild, but
# it proves we distinguish between text parts based on 'start'. The
# content would not, of course, actually work :)
'related_with_start': (
(0, 2, None),
(1,),
(1, 2),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/related; boundary="==="; type=text/html;
start="<body>"
--===
Content-Type: text/html
Content-ID: <include>
useless text
--===
Content-Type: text/html
Content-ID: <body>
<p>simple body</p>
<!--#include file="<include>"-->
--===--
""")),
'mixed_alternative_plain_related': (
(3, 4, 2),
(6, 7),
(1, 6, 7),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==="
--===
Content-Type: multipart/alternative; boundary="+++"
--+++
Content-Type: text/plain
simple body
--+++
Content-Type: multipart/related; boundary="___"
--___
Content-Type: text/html
<p>simple body</p>
--___
Content-Type: image/jpg
Content-ID: <image1@cid>
bogus jpg body
--___--
--+++--
--===
Content-Type: image/jpg
Content-Disposition: attachment
bogus jpg body
--===
Content-Type: image/jpg
Content-Disposition: AttacHmenT
another bogus jpg body
--===--
""")),
# This structure suggested by Stephen J. Turnbull...may not exist/be
# supported in the wild, but we want to support it.
'mixed_related_alternative_plain_html': (
(1, 4, 3),
(6, 7),
(1, 6, 7),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==="
--===
Content-Type: multipart/related; boundary="+++"
--+++
Content-Type: multipart/alternative; boundary="___"
--___
Content-Type: text/plain
simple body
--___
Content-Type: text/html
<p>simple body</p>
--___--
--+++
Content-Type: image/jpg
Content-ID: <image1@cid>
bogus jpg body
--+++--
--===
Content-Type: image/jpg
Content-Disposition: attachment
bogus jpg body
--===
Content-Type: image/jpg
Content-Disposition: attachment
another bogus jpg body
--===--
""")),
# Same thing, but proving we only look at the root part, which is the
# first one if there isn't any start parameter. That is, this is a
# broken related.
'mixed_related_alternative_plain_html_wrong_order': (
(1, None, None),
(6, 7),
(1, 6, 7),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==="
--===
Content-Type: multipart/related; boundary="+++"
--+++
Content-Type: image/jpg
Content-ID: <image1@cid>
bogus jpg body
--+++
Content-Type: multipart/alternative; boundary="___"
--___
Content-Type: text/plain
simple body
--___
Content-Type: text/html
<p>simple body</p>
--___--
--+++--
--===
Content-Type: image/jpg
Content-Disposition: attachment
bogus jpg body
--===
Content-Type: image/jpg
Content-Disposition: attachment
another bogus jpg body
--===--
""")),
'message_rfc822': (
(None, None, None),
(),
(),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: message/rfc822
To: bar@example.com
From: robot@examp.com
this is a message body.
""")),
'mixed_text_message_rfc822': (
(None, None, 1),
(2,),
(1, 2),
textwrap.dedent("""\
To: foo@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==="
--===
Content-Type: text/plain
Your message has bounced, ser.
--===
Content-Type: message/rfc822
To: bar@example.com
From: robot@examp.com
this is a message body.
--===--
""")),
}
def message_as_get_body(self, body_parts, attachments, parts, msg):
m = self._str_msg(msg)
allparts = list(m.walk())
expected = [None if n is None else allparts[n] for n in body_parts]
related = 0; html = 1; plain = 2
self.assertEqual(m.get_body(), first(expected))
self.assertEqual(m.get_body(preferencelist=(
'related', 'html', 'plain')),
first(expected))
self.assertEqual(m.get_body(preferencelist=('related', 'html')),
first(expected[related:html+1]))
self.assertEqual(m.get_body(preferencelist=('related', 'plain')),
first([expected[related], expected[plain]]))
self.assertEqual(m.get_body(preferencelist=('html', 'plain')),
first(expected[html:plain+1]))
self.assertEqual(m.get_body(preferencelist=['related']),
expected[related])
self.assertEqual(m.get_body(preferencelist=['html']), expected[html])
self.assertEqual(m.get_body(preferencelist=['plain']), expected[plain])
self.assertEqual(m.get_body(preferencelist=('plain', 'html')),
first(expected[plain:html-1:-1]))
self.assertEqual(m.get_body(preferencelist=('plain', 'related')),
first([expected[plain], expected[related]]))
self.assertEqual(m.get_body(preferencelist=('html', 'related')),
first(expected[html::-1]))
self.assertEqual(m.get_body(preferencelist=('plain', 'html', 'related')),
first(expected[::-1]))
self.assertEqual(m.get_body(preferencelist=('html', 'plain', 'related')),
first([expected[html],
expected[plain],
expected[related]]))
def message_as_iter_attachment(self, body_parts, attachments, parts, msg):
m = self._str_msg(msg)
allparts = list(m.walk())
attachments = [allparts[n] for n in attachments]
self.assertEqual(list(m.iter_attachments()), attachments)
def message_as_iter_parts(self, body_parts, attachments, parts, msg):
m = self._str_msg(msg)
allparts = list(m.walk())
parts = [allparts[n] for n in parts]
self.assertEqual(list(m.iter_parts()), parts)
class _TestContentManager:
def get_content(self, msg, *args, **kw):
return msg, args, kw
def set_content(self, msg, *args, **kw):
self.msg = msg
self.args = args
self.kw = kw
def test_get_content_with_cm(self):
m = self._str_msg('')
cm = self._TestContentManager()
self.assertEqual(m.get_content(content_manager=cm), (m, (), {}))
msg, args, kw = m.get_content('foo', content_manager=cm, bar=1, k=2)
self.assertEqual(msg, m)
self.assertEqual(args, ('foo',))
self.assertEqual(kw, dict(bar=1, k=2))
def test_get_content_default_cm_comes_from_policy(self):
p = policy.default.clone(content_manager=self._TestContentManager())
m = self._str_msg('', policy=p)
self.assertEqual(m.get_content(), (m, (), {}))
msg, args, kw = m.get_content('foo', bar=1, k=2)
self.assertEqual(msg, m)
self.assertEqual(args, ('foo',))
self.assertEqual(kw, dict(bar=1, k=2))
def test_set_content_with_cm(self):
m = self._str_msg('')
cm = self._TestContentManager()
m.set_content(content_manager=cm)
self.assertEqual(cm.msg, m)
self.assertEqual(cm.args, ())
self.assertEqual(cm.kw, {})
m.set_content('foo', content_manager=cm, bar=1, k=2)
self.assertEqual(cm.msg, m)
self.assertEqual(cm.args, ('foo',))
self.assertEqual(cm.kw, dict(bar=1, k=2))
def test_set_content_default_cm_comes_from_policy(self):
cm = self._TestContentManager()
p = policy.default.clone(content_manager=cm)
m = self._str_msg('', policy=p)
m.set_content()
self.assertEqual(cm.msg, m)
self.assertEqual(cm.args, ())
self.assertEqual(cm.kw, {})
m.set_content('foo', bar=1, k=2)
self.assertEqual(cm.msg, m)
self.assertEqual(cm.args, ('foo',))
self.assertEqual(cm.kw, dict(bar=1, k=2))
# outcome is whether xxx_method should raise ValueError error when called
# on multipart/subtype. Blank outcome means it depends on xxx (add
# succeeds, make raises). Note: 'none' means there are content-type
# headers but payload is None...this happening in practice would be very
# unusual, so treating it as if there were content seems reasonable.
# method subtype outcome
subtype_params = (
('related', 'no_content', 'succeeds'),
('related', 'none', 'succeeds'),
('related', 'plain', 'succeeds'),
('related', 'related', ''),
('related', 'alternative', 'raises'),
('related', 'mixed', 'raises'),
('alternative', 'no_content', 'succeeds'),
('alternative', 'none', 'succeeds'),
('alternative', 'plain', 'succeeds'),
('alternative', 'related', 'succeeds'),
('alternative', 'alternative', ''),
('alternative', 'mixed', 'raises'),
('mixed', 'no_content', 'succeeds'),
('mixed', 'none', 'succeeds'),
('mixed', 'plain', 'succeeds'),
('mixed', 'related', 'succeeds'),
('mixed', 'alternative', 'succeeds'),
('mixed', 'mixed', ''),
)
def _make_subtype_test_message(self, subtype):
m = self.message()
payload = None
msg_headers = [
('To', 'foo@bar.com'),
('From', 'bar@foo.com'),
]
if subtype != 'no_content':
('content-shadow', 'Logrus'),
msg_headers.append(('X-Random-Header', 'Corwin'))
if subtype == 'text':
payload = ''
msg_headers.append(('Content-Type', 'text/plain'))
m.set_payload('')
elif subtype != 'no_content':
payload = []
msg_headers.append(('Content-Type', 'multipart/' + subtype))
msg_headers.append(('X-Trump', 'Random'))
m.set_payload(payload)
for name, value in msg_headers:
m[name] = value
return m, msg_headers, payload
def _check_disallowed_subtype_raises(self, m, method_name, subtype, method):
with self.assertRaises(ValueError) as ar:
getattr(m, method)()
exc_text = str(ar.exception)
self.assertIn(subtype, exc_text)
self.assertIn(method_name, exc_text)
def _check_make_multipart(self, m, msg_headers, payload):
count = 0
for name, value in msg_headers:
if not name.lower().startswith('content-'):
self.assertEqual(m[name], value)
count += 1
self.assertEqual(len(m), count+1) # +1 for new Content-Type
part = next(m.iter_parts())
count = 0
for name, value in msg_headers:
if name.lower().startswith('content-'):
self.assertEqual(part[name], value)
count += 1
self.assertEqual(len(part), count)
self.assertEqual(part.get_payload(), payload)
def subtype_as_make(self, method, subtype, outcome):
m, msg_headers, payload = self._make_subtype_test_message(subtype)
make_method = 'make_' + method
if outcome in ('', 'raises'):
self._check_disallowed_subtype_raises(m, method, subtype, make_method)
return
getattr(m, make_method)()
self.assertEqual(m.get_content_maintype(), 'multipart')
self.assertEqual(m.get_content_subtype(), method)
if subtype == 'no_content':
self.assertEqual(len(m.get_payload()), 0)
self.assertEqual(m.items(),
msg_headers + [('Content-Type',
'multipart/'+method)])
else:
self.assertEqual(len(m.get_payload()), 1)
self._check_make_multipart(m, msg_headers, payload)
def subtype_as_make_with_boundary(self, method, subtype, outcome):
# Doing all variation is a bit of overkill...
m = self.message()
if outcome in ('', 'raises'):
m['Content-Type'] = 'multipart/' + subtype
with self.assertRaises(ValueError) as cm:
getattr(m, 'make_' + method)()
return
if subtype == 'plain':
m['Content-Type'] = 'text/plain'
elif subtype != 'no_content':
m['Content-Type'] = 'multipart/' + subtype
getattr(m, 'make_' + method)(boundary="abc")
self.assertTrue(m.is_multipart())
self.assertEqual(m.get_boundary(), 'abc')
def test_policy_on_part_made_by_make_comes_from_message(self):
for method in ('make_related', 'make_alternative', 'make_mixed'):
m = self.message(policy=self.policy.clone(content_manager='foo'))
m['Content-Type'] = 'text/plain'
getattr(m, method)()
self.assertEqual(m.get_payload(0).policy.content_manager, 'foo')
class _TestSetContentManager:
def set_content(self, msg, content, *args, **kw):
msg['Content-Type'] = 'text/plain'
msg.set_payload(content)
def subtype_as_add(self, method, subtype, outcome):
m, msg_headers, payload = self._make_subtype_test_message(subtype)
cm = self._TestSetContentManager()
add_method = 'add_attachment' if method=='mixed' else 'add_' + method
if outcome == 'raises':
self._check_disallowed_subtype_raises(m, method, subtype, add_method)
return
getattr(m, add_method)('test', content_manager=cm)
self.assertEqual(m.get_content_maintype(), 'multipart')
self.assertEqual(m.get_content_subtype(), method)
if method == subtype or subtype == 'no_content':
self.assertEqual(len(m.get_payload()), 1)
for name, value in msg_headers:
self.assertEqual(m[name], value)
part = m.get_payload()[0]
else:
self.assertEqual(len(m.get_payload()), 2)
self._check_make_multipart(m, msg_headers, payload)
part = m.get_payload()[1]
self.assertEqual(part.get_content_type(), 'text/plain')
self.assertEqual(part.get_payload(), 'test')
if method=='mixed':
self.assertEqual(part['Content-Disposition'], 'attachment')
elif method=='related':
self.assertEqual(part['Content-Disposition'], 'inline')
else:
# Otherwise we don't guess.
self.assertIsNone(part['Content-Disposition'])
class _TestSetRaisingContentManager:
def set_content(self, msg, content, *args, **kw):
raise Exception('test')
def test_default_content_manager_for_add_comes_from_policy(self):
cm = self._TestSetRaisingContentManager()
m = self.message(policy=self.policy.clone(content_manager=cm))
for method in ('add_related', 'add_alternative', 'add_attachment'):
with self.assertRaises(Exception) as ar:
getattr(m, method)('')
self.assertEqual(str(ar.exception), 'test')
def message_as_clear(self, body_parts, attachments, parts, msg):
m = self._str_msg(msg)
m.clear()
self.assertEqual(len(m), 0)
self.assertEqual(list(m.items()), [])
self.assertIsNone(m.get_payload())
self.assertEqual(list(m.iter_parts()), [])
def message_as_clear_content(self, body_parts, attachments, parts, msg):
m = self._str_msg(msg)
expected_headers = [h for h in m.keys()
if not h.lower().startswith('content-')]
m.clear_content()
self.assertEqual(list(m.keys()), expected_headers)
self.assertIsNone(m.get_payload())
self.assertEqual(list(m.iter_parts()), [])
def test_is_attachment(self):
m = self._make_message()
self.assertFalse(m.is_attachment())
m['Content-Disposition'] = 'inline'
self.assertFalse(m.is_attachment())
m.replace_header('Content-Disposition', 'attachment')
self.assertTrue(m.is_attachment())
m.replace_header('Content-Disposition', 'AtTachMent')
self.assertTrue(m.is_attachment())
m.set_param('filename', 'abc.png', 'Content-Disposition')
self.assertTrue(m.is_attachment())
def test_iter_attachments_mutation(self):
# We had a bug where iter_attachments was mutating the list.
m = self._make_message()
m.set_content('arbitrary text as main part')
m.add_related('more text as a related part')
m.add_related('yet more text as a second "attachment"')
orig = m.get_payload().copy()
self.assertEqual(len(list(m.iter_attachments())), 2)
self.assertEqual(m.get_payload(), orig)
class TestEmailMessage(TestEmailMessageBase, TestEmailBase):
message = EmailMessage
def test_set_content_adds_MIME_Version(self):
m = self._str_msg('')
cm = self._TestContentManager()
self.assertNotIn('MIME-Version', m)
m.set_content(content_manager=cm)
self.assertEqual(m['MIME-Version'], '1.0')
class _MIME_Version_adding_CM:
def set_content(self, msg, *args, **kw):
msg['MIME-Version'] = '1.0'
def test_set_content_does_not_duplicate_MIME_Version(self):
m = self._str_msg('')
cm = self._MIME_Version_adding_CM()
self.assertNotIn('MIME-Version', m)
m.set_content(content_manager=cm)
self.assertEqual(m['MIME-Version'], '1.0')
def test_as_string_uses_max_header_length_by_default(self):
m = self._str_msg('Subject: long line' + ' ab'*50 + '\n\n')
self.assertEqual(len(m.as_string().strip().splitlines()), 3)
def test_as_string_allows_maxheaderlen(self):
m = self._str_msg('Subject: long line' + ' ab'*50 + '\n\n')
self.assertEqual(len(m.as_string(maxheaderlen=0).strip().splitlines()),
1)
self.assertEqual(len(m.as_string(maxheaderlen=34).strip().splitlines()),
6)
def test_str_defaults_to_policy_max_line_length(self):
m = self._str_msg('Subject: long line' + ' ab'*50 + '\n\n')
self.assertEqual(len(str(m).strip().splitlines()), 3)
def test_str_defaults_to_utf8(self):
m = EmailMessage()
m['Subject'] = 'unicöde'
self.assertEqual(str(m), 'Subject: unicöde\n\n')
class TestMIMEPart(TestEmailMessageBase, TestEmailBase):
# Doing the full test run here may seem a bit redundant, since the two
# classes are almost identical. But what if they drift apart? So we do
# the full tests so that any future drift doesn't introduce bugs.
message = MIMEPart
def test_set_content_does_not_add_MIME_Version(self):
m = self._str_msg('')
cm = self._TestContentManager()
self.assertNotIn('MIME-Version', m)
m.set_content(content_manager=cm)
self.assertNotIn('MIME-Version', m)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,110 @@
import io
import email
import unittest
from email.message import Message, EmailMessage
from email.policy import default
from test.test_email import TestEmailBase
class TestCustomMessage(TestEmailBase):
class MyMessage(Message):
def __init__(self, policy):
self.check_policy = policy
super().__init__()
MyPolicy = TestEmailBase.policy.clone(linesep='boo')
def test_custom_message_gets_policy_if_possible_from_string(self):
msg = email.message_from_string("Subject: bogus\n\nmsg\n",
self.MyMessage,
policy=self.MyPolicy)
self.assertIsInstance(msg, self.MyMessage)
self.assertIs(msg.check_policy, self.MyPolicy)
def test_custom_message_gets_policy_if_possible_from_file(self):
source_file = io.StringIO("Subject: bogus\n\nmsg\n")
msg = email.message_from_file(source_file,
self.MyMessage,
policy=self.MyPolicy)
self.assertIsInstance(msg, self.MyMessage)
self.assertIs(msg.check_policy, self.MyPolicy)
# XXX add tests for other functions that take Message arg.
class TestParserBase:
def test_only_split_on_cr_lf(self):
# The unicode line splitter splits on unicode linebreaks, which are
# more numerous than allowed by the email RFCs; make sure we are only
# splitting on those two.
for parser in self.parsers:
with self.subTest(parser=parser.__name__):
msg = parser(
"Next-Line: not\x85broken\r\n"
"Null: not\x00broken\r\n"
"Vertical-Tab: not\vbroken\r\n"
"Form-Feed: not\fbroken\r\n"
"File-Separator: not\x1Cbroken\r\n"
"Group-Separator: not\x1Dbroken\r\n"
"Record-Separator: not\x1Ebroken\r\n"
"Line-Separator: not\u2028broken\r\n"
"Paragraph-Separator: not\u2029broken\r\n"
"\r\n",
policy=default,
)
self.assertEqual(msg.items(), [
("Next-Line", "not\x85broken"),
("Null", "not\x00broken"),
("Vertical-Tab", "not\vbroken"),
("Form-Feed", "not\fbroken"),
("File-Separator", "not\x1Cbroken"),
("Group-Separator", "not\x1Dbroken"),
("Record-Separator", "not\x1Ebroken"),
("Line-Separator", "not\u2028broken"),
("Paragraph-Separator", "not\u2029broken"),
])
self.assertEqual(msg.get_payload(), "")
class MyMessage(EmailMessage):
pass
def test_custom_message_factory_on_policy(self):
for parser in self.parsers:
with self.subTest(parser=parser.__name__):
MyPolicy = default.clone(message_factory=self.MyMessage)
msg = parser("To: foo\n\ntest", policy=MyPolicy)
self.assertIsInstance(msg, self.MyMessage)
def test_factory_arg_overrides_policy(self):
for parser in self.parsers:
with self.subTest(parser=parser.__name__):
MyPolicy = default.clone(message_factory=self.MyMessage)
msg = parser("To: foo\n\ntest", Message, policy=MyPolicy)
self.assertNotIsInstance(msg, self.MyMessage)
self.assertIsInstance(msg, Message)
# Play some games to get nice output in subTest. This code could be clearer
# if staticmethod supported __name__.
def message_from_file(s, *args, **kw):
f = io.StringIO(s)
return email.message_from_file(f, *args, **kw)
class TestParser(TestParserBase, TestEmailBase):
parsers = (email.message_from_string, message_from_file)
def message_from_bytes(s, *args, **kw):
return email.message_from_bytes(s.encode(), *args, **kw)
def message_from_binary_file(s, *args, **kw):
f = io.BytesIO(s.encode())
return email.message_from_binary_file(f, *args, **kw)
class TestBytesParser(TestParserBase, TestEmailBase):
parsers = (message_from_bytes, message_from_binary_file)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,76 @@
import unittest
import textwrap
import copy
import pickle
import email
import email.message
from email import policy
from email.headerregistry import HeaderRegistry
from test.test_email import TestEmailBase, parameterize
@parameterize
class TestPickleCopyHeader(TestEmailBase):
header_factory = HeaderRegistry()
unstructured = header_factory('subject', 'this is a test')
header_params = {
'subject': ('subject', 'this is a test'),
'from': ('from', 'frodo@mordor.net'),
'to': ('to', 'a: k@b.com, y@z.com;, j@f.com'),
'date': ('date', 'Tue, 29 May 2012 09:24:26 +1000'),
}
def header_as_deepcopy(self, name, value):
header = self.header_factory(name, value)
h = copy.deepcopy(header)
self.assertEqual(str(h), str(header))
def header_as_pickle(self, name, value):
header = self.header_factory(name, value)
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
p = pickle.dumps(header, proto)
h = pickle.loads(p)
self.assertEqual(str(h), str(header))
@parameterize
class TestPickleCopyMessage(TestEmailBase):
# Message objects are a sequence, so we have to make them a one-tuple in
# msg_params so they get passed to the parameterized test method as a
# single argument instead of as a list of headers.
msg_params = {}
# Note: there will be no custom header objects in the parsed message.
msg_params['parsed'] = (email.message_from_string(textwrap.dedent("""\
Date: Tue, 29 May 2012 09:24:26 +1000
From: frodo@mordor.net
To: bilbo@underhill.org
Subject: help
I think I forgot the ring.
"""), policy=policy.default),)
msg_params['created'] = (email.message.Message(policy=policy.default),)
msg_params['created'][0]['Date'] = 'Tue, 29 May 2012 09:24:26 +1000'
msg_params['created'][0]['From'] = 'frodo@mordor.net'
msg_params['created'][0]['To'] = 'bilbo@underhill.org'
msg_params['created'][0]['Subject'] = 'help'
msg_params['created'][0].set_payload('I think I forgot the ring.')
def msg_as_deepcopy(self, msg):
msg2 = copy.deepcopy(msg)
self.assertEqual(msg2.as_string(), msg.as_string())
def msg_as_pickle(self, msg):
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
p = pickle.dumps(msg, proto)
msg2 = pickle.loads(p)
self.assertEqual(msg2.as_string(), msg.as_string())
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,374 @@
import io
import types
import textwrap
import unittest
import email.errors
import email.policy
import email.parser
import email.generator
import email.message
from email import headerregistry
def make_defaults(base_defaults, differences):
defaults = base_defaults.copy()
defaults.update(differences)
return defaults
class PolicyAPITests(unittest.TestCase):
longMessage = True
# Base default values.
compat32_defaults = {
'max_line_length': 78,
'linesep': '\n',
'cte_type': '8bit',
'raise_on_defect': False,
'mangle_from_': True,
'message_factory': None,
}
# These default values are the ones set on email.policy.default.
# If any of these defaults change, the docs must be updated.
policy_defaults = compat32_defaults.copy()
policy_defaults.update({
'utf8': False,
'raise_on_defect': False,
'header_factory': email.policy.EmailPolicy.header_factory,
'refold_source': 'long',
'content_manager': email.policy.EmailPolicy.content_manager,
'mangle_from_': False,
'message_factory': email.message.EmailMessage,
})
# For each policy under test, we give here what we expect the defaults to
# be for that policy. The second argument to make defaults is the
# difference between the base defaults and that for the particular policy.
new_policy = email.policy.EmailPolicy()
policies = {
email.policy.compat32: make_defaults(compat32_defaults, {}),
email.policy.default: make_defaults(policy_defaults, {}),
email.policy.SMTP: make_defaults(policy_defaults,
{'linesep': '\r\n'}),
email.policy.SMTPUTF8: make_defaults(policy_defaults,
{'linesep': '\r\n',
'utf8': True}),
email.policy.HTTP: make_defaults(policy_defaults,
{'linesep': '\r\n',
'max_line_length': None}),
email.policy.strict: make_defaults(policy_defaults,
{'raise_on_defect': True}),
new_policy: make_defaults(policy_defaults, {}),
}
# Creating a new policy creates a new header factory. There is a test
# later that proves this.
policies[new_policy]['header_factory'] = new_policy.header_factory
def test_defaults(self):
for policy, expected in self.policies.items():
for attr, value in expected.items():
with self.subTest(policy=policy, attr=attr):
self.assertEqual(getattr(policy, attr), value,
("change {} docs/docstrings if defaults have "
"changed").format(policy))
def test_all_attributes_covered(self):
for policy, expected in self.policies.items():
for attr in dir(policy):
with self.subTest(policy=policy, attr=attr):
if (attr.startswith('_') or
isinstance(getattr(email.policy.EmailPolicy, attr),
types.FunctionType)):
continue
else:
self.assertIn(attr, expected,
"{} is not fully tested".format(attr))
def test_abc(self):
with self.assertRaises(TypeError) as cm:
email.policy.Policy()
msg = str(cm.exception)
abstract_methods = ('fold',
'fold_binary',
'header_fetch_parse',
'header_source_parse',
'header_store_parse')
for method in abstract_methods:
self.assertIn(method, msg)
def test_policy_is_immutable(self):
for policy, defaults in self.policies.items():
for attr in defaults:
with self.assertRaisesRegex(AttributeError, attr+".*read-only"):
setattr(policy, attr, None)
with self.assertRaisesRegex(AttributeError, 'no attribute.*foo'):
policy.foo = None
def test_set_policy_attrs_when_cloned(self):
# None of the attributes has a default value of None, so we set them
# all to None in the clone call and check that it worked.
for policyclass, defaults in self.policies.items():
testattrdict = {attr: None for attr in defaults}
policy = policyclass.clone(**testattrdict)
for attr in defaults:
self.assertIsNone(getattr(policy, attr))
def test_reject_non_policy_keyword_when_called(self):
for policyclass in self.policies:
with self.assertRaises(TypeError):
policyclass(this_keyword_should_not_be_valid=None)
with self.assertRaises(TypeError):
policyclass(newtline=None)
def test_policy_addition(self):
expected = self.policy_defaults.copy()
p1 = email.policy.default.clone(max_line_length=100)
p2 = email.policy.default.clone(max_line_length=50)
added = p1 + p2
expected.update(max_line_length=50)
for attr, value in expected.items():
self.assertEqual(getattr(added, attr), value)
added = p2 + p1
expected.update(max_line_length=100)
for attr, value in expected.items():
self.assertEqual(getattr(added, attr), value)
added = added + email.policy.default
for attr, value in expected.items():
self.assertEqual(getattr(added, attr), value)
def test_register_defect(self):
class Dummy:
def __init__(self):
self.defects = []
obj = Dummy()
defect = object()
policy = email.policy.EmailPolicy()
policy.register_defect(obj, defect)
self.assertEqual(obj.defects, [defect])
defect2 = object()
policy.register_defect(obj, defect2)
self.assertEqual(obj.defects, [defect, defect2])
class MyObj:
def __init__(self):
self.defects = []
class MyDefect(Exception):
pass
def test_handle_defect_raises_on_strict(self):
foo = self.MyObj()
defect = self.MyDefect("the telly is broken")
with self.assertRaisesRegex(self.MyDefect, "the telly is broken"):
email.policy.strict.handle_defect(foo, defect)
def test_handle_defect_registers_defect(self):
foo = self.MyObj()
defect1 = self.MyDefect("one")
email.policy.default.handle_defect(foo, defect1)
self.assertEqual(foo.defects, [defect1])
defect2 = self.MyDefect("two")
email.policy.default.handle_defect(foo, defect2)
self.assertEqual(foo.defects, [defect1, defect2])
class MyPolicy(email.policy.EmailPolicy):
defects = None
def __init__(self, *args, **kw):
super().__init__(*args, defects=[], **kw)
def register_defect(self, obj, defect):
self.defects.append(defect)
def test_overridden_register_defect_still_raises(self):
foo = self.MyObj()
defect = self.MyDefect("the telly is broken")
with self.assertRaisesRegex(self.MyDefect, "the telly is broken"):
self.MyPolicy(raise_on_defect=True).handle_defect(foo, defect)
def test_overridden_register_defect_works(self):
foo = self.MyObj()
defect1 = self.MyDefect("one")
my_policy = self.MyPolicy()
my_policy.handle_defect(foo, defect1)
self.assertEqual(my_policy.defects, [defect1])
self.assertEqual(foo.defects, [])
defect2 = self.MyDefect("two")
my_policy.handle_defect(foo, defect2)
self.assertEqual(my_policy.defects, [defect1, defect2])
self.assertEqual(foo.defects, [])
def test_default_header_factory(self):
h = email.policy.default.header_factory('Test', 'test')
self.assertEqual(h.name, 'Test')
self.assertIsInstance(h, headerregistry.UnstructuredHeader)
self.assertIsInstance(h, headerregistry.BaseHeader)
class Foo:
parse = headerregistry.UnstructuredHeader.parse
def test_each_Policy_gets_unique_factory(self):
policy1 = email.policy.EmailPolicy()
policy2 = email.policy.EmailPolicy()
policy1.header_factory.map_to_type('foo', self.Foo)
h = policy1.header_factory('foo', 'test')
self.assertIsInstance(h, self.Foo)
self.assertNotIsInstance(h, headerregistry.UnstructuredHeader)
h = policy2.header_factory('foo', 'test')
self.assertNotIsInstance(h, self.Foo)
self.assertIsInstance(h, headerregistry.UnstructuredHeader)
def test_clone_copies_factory(self):
policy1 = email.policy.EmailPolicy()
policy2 = policy1.clone()
policy1.header_factory.map_to_type('foo', self.Foo)
h = policy1.header_factory('foo', 'test')
self.assertIsInstance(h, self.Foo)
h = policy2.header_factory('foo', 'test')
self.assertIsInstance(h, self.Foo)
def test_new_factory_overrides_default(self):
mypolicy = email.policy.EmailPolicy()
myfactory = mypolicy.header_factory
newpolicy = mypolicy + email.policy.strict
self.assertEqual(newpolicy.header_factory, myfactory)
newpolicy = email.policy.strict + mypolicy
self.assertEqual(newpolicy.header_factory, myfactory)
def test_adding_default_policies_preserves_default_factory(self):
newpolicy = email.policy.default + email.policy.strict
self.assertEqual(newpolicy.header_factory,
email.policy.EmailPolicy.header_factory)
self.assertEqual(newpolicy.__dict__, {'raise_on_defect': True})
def test_non_ascii_chars_do_not_cause_inf_loop(self):
policy = email.policy.default.clone(max_line_length=20)
actual = policy.fold('Subject', 'ą' * 12)
self.assertEqual(
actual,
'Subject: \n' +
12 * ' =?utf-8?q?=C4=85?=\n')
def test_short_maxlen_error(self):
# RFC 2047 chrome takes up 7 characters, plus the length of the charset
# name, so folding should fail if maxlen is lower than the minimum
# required length for a line.
# Note: This is only triggered when there is a single word longer than
# max_line_length, hence the 1234567890 at the end of this whimsical
# subject. This is because when we encounter a word longer than
# max_line_length, it is broken down into encoded words to fit
# max_line_length. If the max_line_length isn't large enough to even
# contain the RFC 2047 chrome (`?=<charset>?q??=`), we fail.
subject = "Melt away the pounds with this one simple trick! 1234567890"
for maxlen in [3, 7, 9]:
with self.subTest(maxlen=maxlen):
policy = email.policy.default.clone(max_line_length=maxlen)
with self.assertRaises(email.errors.HeaderParseError):
policy.fold("Subject", subject)
# XXX: Need subclassing tests.
# For adding subclassed objects, make sure the usual rules apply (subclass
# wins), but that the order still works (right overrides left).
class TestException(Exception):
pass
class TestPolicyPropagation(unittest.TestCase):
# The abstract methods are used by the parser but not by the wrapper
# functions that call it, so if the exception gets raised we know that the
# policy was actually propagated all the way to feedparser.
class MyPolicy(email.policy.Policy):
def badmethod(self, *args, **kw):
raise TestException("test")
fold = fold_binary = header_fetch_parser = badmethod
header_source_parse = header_store_parse = badmethod
def test_message_from_string(self):
with self.assertRaisesRegex(TestException, "^test$"):
email.message_from_string("Subject: test\n\n",
policy=self.MyPolicy)
def test_message_from_bytes(self):
with self.assertRaisesRegex(TestException, "^test$"):
email.message_from_bytes(b"Subject: test\n\n",
policy=self.MyPolicy)
def test_message_from_file(self):
f = io.StringIO('Subject: test\n\n')
with self.assertRaisesRegex(TestException, "^test$"):
email.message_from_file(f, policy=self.MyPolicy)
def test_message_from_binary_file(self):
f = io.BytesIO(b'Subject: test\n\n')
with self.assertRaisesRegex(TestException, "^test$"):
email.message_from_binary_file(f, policy=self.MyPolicy)
# These are redundant, but we need them for black-box completeness.
def test_parser(self):
p = email.parser.Parser(policy=self.MyPolicy)
with self.assertRaisesRegex(TestException, "^test$"):
p.parsestr('Subject: test\n\n')
def test_bytes_parser(self):
p = email.parser.BytesParser(policy=self.MyPolicy)
with self.assertRaisesRegex(TestException, "^test$"):
p.parsebytes(b'Subject: test\n\n')
# Now that we've established that all the parse methods get the
# policy in to feedparser, we can use message_from_string for
# the rest of the propagation tests.
def _make_msg(self, source='Subject: test\n\n', policy=None):
self.policy = email.policy.default.clone() if policy is None else policy
return email.message_from_string(source, policy=self.policy)
def test_parser_propagates_policy_to_message(self):
msg = self._make_msg()
self.assertIs(msg.policy, self.policy)
def test_parser_propagates_policy_to_sub_messages(self):
msg = self._make_msg(textwrap.dedent("""\
Subject: mime test
MIME-Version: 1.0
Content-Type: multipart/mixed, boundary="XXX"
--XXX
Content-Type: text/plain
test
--XXX
Content-Type: text/plain
test2
--XXX--
"""))
for part in msg.walk():
self.assertIs(part.policy, self.policy)
def test_message_policy_propagates_to_generator(self):
msg = self._make_msg("Subject: test\nTo: foo\n\n",
policy=email.policy.default.clone(linesep='X'))
s = io.StringIO()
g = email.generator.Generator(s)
g.flatten(msg)
self.assertEqual(s.getvalue(), "Subject: testXTo: fooXX")
def test_message_policy_used_by_as_string(self):
msg = self._make_msg("Subject: test\nTo: foo\n\n",
policy=email.policy.default.clone(linesep='X'))
self.assertEqual(msg.as_string(), "Subject: testXTo: fooXX")
class TestConcretePolicies(unittest.TestCase):
def test_header_store_parse_rejects_newlines(self):
instance = email.policy.EmailPolicy()
self.assertRaises(ValueError,
instance.header_store_parse,
'From', 'spam\negg@foo.py')
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,165 @@
import datetime
from email import utils
import test.support
import time
import unittest
import sys
import os.path
class DateTimeTests(unittest.TestCase):
datestring = 'Sun, 23 Sep 2001 20:10:55'
dateargs = (2001, 9, 23, 20, 10, 55)
offsetstring = ' -0700'
utcoffset = datetime.timedelta(hours=-7)
tz = datetime.timezone(utcoffset)
naive_dt = datetime.datetime(*dateargs)
aware_dt = datetime.datetime(*dateargs, tzinfo=tz)
def test_naive_datetime(self):
self.assertEqual(utils.format_datetime(self.naive_dt),
self.datestring + ' -0000')
def test_aware_datetime(self):
self.assertEqual(utils.format_datetime(self.aware_dt),
self.datestring + self.offsetstring)
def test_usegmt(self):
utc_dt = datetime.datetime(*self.dateargs,
tzinfo=datetime.timezone.utc)
self.assertEqual(utils.format_datetime(utc_dt, usegmt=True),
self.datestring + ' GMT')
def test_usegmt_with_naive_datetime_raises(self):
with self.assertRaises(ValueError):
utils.format_datetime(self.naive_dt, usegmt=True)
def test_usegmt_with_non_utc_datetime_raises(self):
with self.assertRaises(ValueError):
utils.format_datetime(self.aware_dt, usegmt=True)
def test_parsedate_to_datetime(self):
self.assertEqual(
utils.parsedate_to_datetime(self.datestring + self.offsetstring),
self.aware_dt)
def test_parsedate_to_datetime_naive(self):
self.assertEqual(
utils.parsedate_to_datetime(self.datestring + ' -0000'),
self.naive_dt)
class LocaltimeTests(unittest.TestCase):
def test_localtime_is_tz_aware_daylight_true(self):
test.support.patch(self, time, 'daylight', True)
t = utils.localtime()
self.assertIsNotNone(t.tzinfo)
def test_localtime_is_tz_aware_daylight_false(self):
test.support.patch(self, time, 'daylight', False)
t = utils.localtime()
self.assertIsNotNone(t.tzinfo)
def test_localtime_daylight_true_dst_false(self):
test.support.patch(self, time, 'daylight', True)
t0 = datetime.datetime(2012, 3, 12, 1, 1)
t1 = utils.localtime(t0, isdst=-1)
t2 = utils.localtime(t1)
self.assertEqual(t1, t2)
def test_localtime_daylight_false_dst_false(self):
test.support.patch(self, time, 'daylight', False)
t0 = datetime.datetime(2012, 3, 12, 1, 1)
t1 = utils.localtime(t0, isdst=-1)
t2 = utils.localtime(t1)
self.assertEqual(t1, t2)
@test.support.run_with_tz('Europe/Minsk')
def test_localtime_daylight_true_dst_true(self):
test.support.patch(self, time, 'daylight', True)
t0 = datetime.datetime(2012, 3, 12, 1, 1)
t1 = utils.localtime(t0, isdst=1)
t2 = utils.localtime(t1)
self.assertEqual(t1, t2)
@test.support.run_with_tz('Europe/Minsk')
def test_localtime_daylight_false_dst_true(self):
test.support.patch(self, time, 'daylight', False)
t0 = datetime.datetime(2012, 3, 12, 1, 1)
t1 = utils.localtime(t0, isdst=1)
t2 = utils.localtime(t1)
self.assertEqual(t1, t2)
@test.support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
def test_localtime_epoch_utc_daylight_true(self):
test.support.patch(self, time, 'daylight', True)
t0 = datetime.datetime(1990, 1, 1, tzinfo = datetime.timezone.utc)
t1 = utils.localtime(t0)
t2 = t0 - datetime.timedelta(hours=5)
t2 = t2.replace(tzinfo = datetime.timezone(datetime.timedelta(hours=-5)))
self.assertEqual(t1, t2)
@test.support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
def test_localtime_epoch_utc_daylight_false(self):
test.support.patch(self, time, 'daylight', False)
t0 = datetime.datetime(1990, 1, 1, tzinfo = datetime.timezone.utc)
t1 = utils.localtime(t0)
t2 = t0 - datetime.timedelta(hours=5)
t2 = t2.replace(tzinfo = datetime.timezone(datetime.timedelta(hours=-5)))
self.assertEqual(t1, t2)
def test_localtime_epoch_notz_daylight_true(self):
test.support.patch(self, time, 'daylight', True)
t0 = datetime.datetime(1990, 1, 1)
t1 = utils.localtime(t0)
t2 = utils.localtime(t0.replace(tzinfo=None))
self.assertEqual(t1, t2)
def test_localtime_epoch_notz_daylight_false(self):
test.support.patch(self, time, 'daylight', False)
t0 = datetime.datetime(1990, 1, 1)
t1 = utils.localtime(t0)
t2 = utils.localtime(t0.replace(tzinfo=None))
self.assertEqual(t1, t2)
# XXX: Need a more robust test for Olson's tzdata
@unittest.skipIf(sys.platform.startswith('win'),
"Windows does not use Olson's TZ database")
@unittest.skipUnless(os.path.exists('/usr/share/zoneinfo') or
os.path.exists('/usr/lib/zoneinfo'),
"Can't find the Olson's TZ database")
@test.support.run_with_tz('Europe/Kiev')
def test_variable_tzname(self):
t0 = datetime.datetime(1984, 1, 1, tzinfo=datetime.timezone.utc)
t1 = utils.localtime(t0)
self.assertEqual(t1.tzname(), 'MSK')
t0 = datetime.datetime(1994, 1, 1, tzinfo=datetime.timezone.utc)
t1 = utils.localtime(t0)
self.assertEqual(t1.tzname(), 'EET')
# Issue #24836: The timezone files are out of date (pre 2011k)
# on Mac OS X Snow Leopard.
@test.support.requires_mac_ver(10, 7)
class FormatDateTests(unittest.TestCase):
@test.support.run_with_tz('Europe/Minsk')
def test_formatdate(self):
timeval = time.mktime((2011, 12, 1, 18, 0, 0, 4, 335, 0))
string = utils.formatdate(timeval, localtime=False, usegmt=False)
self.assertEqual(string, 'Thu, 01 Dec 2011 15:00:00 -0000')
string = utils.formatdate(timeval, localtime=False, usegmt=True)
self.assertEqual(string, 'Thu, 01 Dec 2011 15:00:00 GMT')
@test.support.run_with_tz('Europe/Minsk')
def test_formatdate_with_localtime(self):
timeval = time.mktime((2011, 1, 1, 18, 0, 0, 6, 1, 0))
string = utils.formatdate(timeval, localtime=True)
self.assertEqual(string, 'Sat, 01 Jan 2011 18:00:00 +0200')
# Minsk moved from +0200 (with DST) to +0300 (without DST) in 2011
timeval = time.mktime((2011, 12, 1, 18, 0, 0, 4, 335, 0))
string = utils.formatdate(timeval, localtime=True)
self.assertEqual(string, 'Thu, 01 Dec 2011 18:00:00 +0300')
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,133 @@
# Copyright (C) 2002-2004 Python Software Foundation
#
# A torture test of the email package. This should not be run as part of the
# standard Python test suite since it requires several meg of email messages
# collected in the wild. These source messages are not checked into the
# Python distro, but are available as part of the standalone email package at
# http://sf.net/projects/mimelib
import sys
import os
import unittest
from io import StringIO
from test.test_email import TestEmailBase
from test.support import run_unittest
import email
from email import __file__ as testfile
from email.iterators import _structure
def openfile(filename):
from os.path import join, dirname, abspath
path = abspath(join(dirname(testfile), os.pardir, 'moredata', filename))
return open(path, 'r')
# Prevent this test from running in the Python distro
try:
openfile('crispin-torture.txt')
except OSError:
raise unittest.SkipTest
class TortureBase(TestEmailBase):
def _msgobj(self, filename):
fp = openfile(filename)
try:
msg = email.message_from_file(fp)
finally:
fp.close()
return msg
class TestCrispinTorture(TortureBase):
# Mark Crispin's torture test from the SquirrelMail project
def test_mondo_message(self):
eq = self.assertEqual
neq = self.ndiffAssertEqual
msg = self._msgobj('crispin-torture.txt')
payload = msg.get_payload()
eq(type(payload), list)
eq(len(payload), 12)
eq(msg.preamble, None)
eq(msg.epilogue, '\n')
# Probably the best way to verify the message is parsed correctly is to
# dump its structure and compare it against the known structure.
fp = StringIO()
_structure(msg, fp=fp)
neq(fp.getvalue(), """\
multipart/mixed
text/plain
message/rfc822
multipart/alternative
text/plain
multipart/mixed
text/richtext
application/andrew-inset
message/rfc822
audio/basic
audio/basic
image/pbm
message/rfc822
multipart/mixed
multipart/mixed
text/plain
audio/x-sun
multipart/mixed
image/gif
image/gif
application/x-be2
application/atomicmail
audio/x-sun
message/rfc822
multipart/mixed
text/plain
image/pgm
text/plain
message/rfc822
multipart/mixed
text/plain
image/pbm
message/rfc822
application/postscript
image/gif
message/rfc822
multipart/mixed
audio/basic
audio/basic
message/rfc822
multipart/mixed
application/postscript
text/plain
message/rfc822
multipart/mixed
text/plain
multipart/parallel
image/gif
audio/basic
application/atomicmail
message/rfc822
audio/x-sun
""")
def _testclasses():
mod = sys.modules[__name__]
return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')]
def suite():
suite = unittest.TestSuite()
for testclass in _testclasses():
suite.addTest(unittest.makeSuite(testclass))
return suite
def test_main():
for testclass in _testclasses():
run_unittest(testclass)
if __name__ == '__main__':
unittest.main(defaultTest='suite')