headerregistry.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. """Representing and manipulating email headers via custom objects.
  2. This module provides an implementation of the HeaderRegistry API.
  3. The implementation is designed to flexibly follow RFC5322 rules.
  4. """
  5. from types import MappingProxyType
  6. from email import utils
  7. from email import errors
  8. from email import _header_value_parser as parser
  9. class Address:
  10. def __init__(self, display_name='', username='', domain='', addr_spec=None):
  11. """Create an object representing a full email address.
  12. An address can have a 'display_name', a 'username', and a 'domain'. In
  13. addition to specifying the username and domain separately, they may be
  14. specified together by using the addr_spec keyword *instead of* the
  15. username and domain keywords. If an addr_spec string is specified it
  16. must be properly quoted according to RFC 5322 rules; an error will be
  17. raised if it is not.
  18. An Address object has display_name, username, domain, and addr_spec
  19. attributes, all of which are read-only. The addr_spec and the string
  20. value of the object are both quoted according to RFC5322 rules, but
  21. without any Content Transfer Encoding.
  22. """
  23. inputs = ''.join(filter(None, (display_name, username, domain, addr_spec)))
  24. if '\r' in inputs or '\n' in inputs:
  25. raise ValueError("invalid arguments; address parts cannot contain CR or LF")
  26. # This clause with its potential 'raise' may only happen when an
  27. # application program creates an Address object using an addr_spec
  28. # keyword. The email library code itself must always supply username
  29. # and domain.
  30. if addr_spec is not None:
  31. if username or domain:
  32. raise TypeError("addrspec specified when username and/or "
  33. "domain also specified")
  34. a_s, rest = parser.get_addr_spec(addr_spec)
  35. if rest:
  36. raise ValueError("Invalid addr_spec; only '{}' "
  37. "could be parsed from '{}'".format(
  38. a_s, addr_spec))
  39. if a_s.all_defects:
  40. raise a_s.all_defects[0]
  41. username = a_s.local_part
  42. domain = a_s.domain
  43. self._display_name = display_name
  44. self._username = username
  45. self._domain = domain
  46. @property
  47. def display_name(self):
  48. return self._display_name
  49. @property
  50. def username(self):
  51. return self._username
  52. @property
  53. def domain(self):
  54. return self._domain
  55. @property
  56. def addr_spec(self):
  57. """The addr_spec (username@domain) portion of the address, quoted
  58. according to RFC 5322 rules, but with no Content Transfer Encoding.
  59. """
  60. lp = self.username
  61. if not parser.DOT_ATOM_ENDS.isdisjoint(lp):
  62. lp = parser.quote_string(lp)
  63. if self.domain:
  64. return lp + '@' + self.domain
  65. if not lp:
  66. return '<>'
  67. return lp
  68. def __repr__(self):
  69. return "{}(display_name={!r}, username={!r}, domain={!r})".format(
  70. self.__class__.__name__,
  71. self.display_name, self.username, self.domain)
  72. def __str__(self):
  73. disp = self.display_name
  74. if not parser.SPECIALS.isdisjoint(disp):
  75. disp = parser.quote_string(disp)
  76. if disp:
  77. addr_spec = '' if self.addr_spec=='<>' else self.addr_spec
  78. return "{} <{}>".format(disp, addr_spec)
  79. return self.addr_spec
  80. def __eq__(self, other):
  81. if not isinstance(other, Address):
  82. return NotImplemented
  83. return (self.display_name == other.display_name and
  84. self.username == other.username and
  85. self.domain == other.domain)
  86. class Group:
  87. def __init__(self, display_name=None, addresses=None):
  88. """Create an object representing an address group.
  89. An address group consists of a display_name followed by colon and a
  90. list of addresses (see Address) terminated by a semi-colon. The Group
  91. is created by specifying a display_name and a possibly empty list of
  92. Address objects. A Group can also be used to represent a single
  93. address that is not in a group, which is convenient when manipulating
  94. lists that are a combination of Groups and individual Addresses. In
  95. this case the display_name should be set to None. In particular, the
  96. string representation of a Group whose display_name is None is the same
  97. as the Address object, if there is one and only one Address object in
  98. the addresses list.
  99. """
  100. self._display_name = display_name
  101. self._addresses = tuple(addresses) if addresses else tuple()
  102. @property
  103. def display_name(self):
  104. return self._display_name
  105. @property
  106. def addresses(self):
  107. return self._addresses
  108. def __repr__(self):
  109. return "{}(display_name={!r}, addresses={!r}".format(
  110. self.__class__.__name__,
  111. self.display_name, self.addresses)
  112. def __str__(self):
  113. if self.display_name is None and len(self.addresses)==1:
  114. return str(self.addresses[0])
  115. disp = self.display_name
  116. if disp is not None and not parser.SPECIALS.isdisjoint(disp):
  117. disp = parser.quote_string(disp)
  118. adrstr = ", ".join(str(x) for x in self.addresses)
  119. adrstr = ' ' + adrstr if adrstr else adrstr
  120. return "{}:{};".format(disp, adrstr)
  121. def __eq__(self, other):
  122. if not isinstance(other, Group):
  123. return NotImplemented
  124. return (self.display_name == other.display_name and
  125. self.addresses == other.addresses)
  126. # Header Classes #
  127. class BaseHeader(str):
  128. """Base class for message headers.
  129. Implements generic behavior and provides tools for subclasses.
  130. A subclass must define a classmethod named 'parse' that takes an unfolded
  131. value string and a dictionary as its arguments. The dictionary will
  132. contain one key, 'defects', initialized to an empty list. After the call
  133. the dictionary must contain two additional keys: parse_tree, set to the
  134. parse tree obtained from parsing the header, and 'decoded', set to the
  135. string value of the idealized representation of the data from the value.
  136. (That is, encoded words are decoded, and values that have canonical
  137. representations are so represented.)
  138. The defects key is intended to collect parsing defects, which the message
  139. parser will subsequently dispose of as appropriate. The parser should not,
  140. insofar as practical, raise any errors. Defects should be added to the
  141. list instead. The standard header parsers register defects for RFC
  142. compliance issues, for obsolete RFC syntax, and for unrecoverable parsing
  143. errors.
  144. The parse method may add additional keys to the dictionary. In this case
  145. the subclass must define an 'init' method, which will be passed the
  146. dictionary as its keyword arguments. The method should use (usually by
  147. setting them as the value of similarly named attributes) and remove all the
  148. extra keys added by its parse method, and then use super to call its parent
  149. class with the remaining arguments and keywords.
  150. The subclass should also make sure that a 'max_count' attribute is defined
  151. that is either None or 1. XXX: need to better define this API.
  152. """
  153. def __new__(cls, name, value):
  154. kwds = {'defects': []}
  155. cls.parse(value, kwds)
  156. if utils._has_surrogates(kwds['decoded']):
  157. kwds['decoded'] = utils._sanitize(kwds['decoded'])
  158. self = str.__new__(cls, kwds['decoded'])
  159. del kwds['decoded']
  160. self.init(name, **kwds)
  161. return self
  162. def init(self, name, *, parse_tree, defects):
  163. self._name = name
  164. self._parse_tree = parse_tree
  165. self._defects = defects
  166. @property
  167. def name(self):
  168. return self._name
  169. @property
  170. def defects(self):
  171. return tuple(self._defects)
  172. def __reduce__(self):
  173. return (
  174. _reconstruct_header,
  175. (
  176. self.__class__.__name__,
  177. self.__class__.__bases__,
  178. str(self),
  179. ),
  180. self.__getstate__())
  181. @classmethod
  182. def _reconstruct(cls, value):
  183. return str.__new__(cls, value)
  184. def fold(self, *, policy):
  185. """Fold header according to policy.
  186. The parsed representation of the header is folded according to
  187. RFC5322 rules, as modified by the policy. If the parse tree
  188. contains surrogateescaped bytes, the bytes are CTE encoded using
  189. the charset 'unknown-8bit".
  190. Any non-ASCII characters in the parse tree are CTE encoded using
  191. charset utf-8. XXX: make this a policy setting.
  192. The returned value is an ASCII-only string possibly containing linesep
  193. characters, and ending with a linesep character. The string includes
  194. the header name and the ': ' separator.
  195. """
  196. # At some point we need to put fws here if it was in the source.
  197. header = parser.Header([
  198. parser.HeaderLabel([
  199. parser.ValueTerminal(self.name, 'header-name'),
  200. parser.ValueTerminal(':', 'header-sep')]),
  201. ])
  202. if self._parse_tree:
  203. header.append(
  204. parser.CFWSList([parser.WhiteSpaceTerminal(' ', 'fws')]))
  205. header.append(self._parse_tree)
  206. return header.fold(policy=policy)
  207. def _reconstruct_header(cls_name, bases, value):
  208. return type(cls_name, bases, {})._reconstruct(value)
  209. class UnstructuredHeader:
  210. max_count = None
  211. value_parser = staticmethod(parser.get_unstructured)
  212. @classmethod
  213. def parse(cls, value, kwds):
  214. kwds['parse_tree'] = cls.value_parser(value)
  215. kwds['decoded'] = str(kwds['parse_tree'])
  216. class UniqueUnstructuredHeader(UnstructuredHeader):
  217. max_count = 1
  218. class DateHeader:
  219. """Header whose value consists of a single timestamp.
  220. Provides an additional attribute, datetime, which is either an aware
  221. datetime using a timezone, or a naive datetime if the timezone
  222. in the input string is -0000. Also accepts a datetime as input.
  223. The 'value' attribute is the normalized form of the timestamp,
  224. which means it is the output of format_datetime on the datetime.
  225. """
  226. max_count = None
  227. # This is used only for folding, not for creating 'decoded'.
  228. value_parser = staticmethod(parser.get_unstructured)
  229. @classmethod
  230. def parse(cls, value, kwds):
  231. if not value:
  232. kwds['defects'].append(errors.HeaderMissingRequiredValue())
  233. kwds['datetime'] = None
  234. kwds['decoded'] = ''
  235. kwds['parse_tree'] = parser.TokenList()
  236. return
  237. if isinstance(value, str):
  238. kwds['decoded'] = value
  239. try:
  240. value = utils.parsedate_to_datetime(value)
  241. except ValueError:
  242. kwds['defects'].append(errors.InvalidDateDefect('Invalid date value or format'))
  243. kwds['datetime'] = None
  244. kwds['parse_tree'] = parser.TokenList()
  245. return
  246. kwds['datetime'] = value
  247. kwds['decoded'] = utils.format_datetime(kwds['datetime'])
  248. kwds['parse_tree'] = cls.value_parser(kwds['decoded'])
  249. def init(self, *args, **kw):
  250. self._datetime = kw.pop('datetime')
  251. super().init(*args, **kw)
  252. @property
  253. def datetime(self):
  254. return self._datetime
  255. class UniqueDateHeader(DateHeader):
  256. max_count = 1
  257. class AddressHeader:
  258. max_count = None
  259. @staticmethod
  260. def value_parser(value):
  261. address_list, value = parser.get_address_list(value)
  262. assert not value, 'this should not happen'
  263. return address_list
  264. @classmethod
  265. def parse(cls, value, kwds):
  266. if isinstance(value, str):
  267. # We are translating here from the RFC language (address/mailbox)
  268. # to our API language (group/address).
  269. kwds['parse_tree'] = address_list = cls.value_parser(value)
  270. groups = []
  271. for addr in address_list.addresses:
  272. groups.append(Group(addr.display_name,
  273. [Address(mb.display_name or '',
  274. mb.local_part or '',
  275. mb.domain or '')
  276. for mb in addr.all_mailboxes]))
  277. defects = list(address_list.all_defects)
  278. else:
  279. # Assume it is Address/Group stuff
  280. if not hasattr(value, '__iter__'):
  281. value = [value]
  282. groups = [Group(None, [item]) if not hasattr(item, 'addresses')
  283. else item
  284. for item in value]
  285. defects = []
  286. kwds['groups'] = groups
  287. kwds['defects'] = defects
  288. kwds['decoded'] = ', '.join([str(item) for item in groups])
  289. if 'parse_tree' not in kwds:
  290. kwds['parse_tree'] = cls.value_parser(kwds['decoded'])
  291. def init(self, *args, **kw):
  292. self._groups = tuple(kw.pop('groups'))
  293. self._addresses = None
  294. super().init(*args, **kw)
  295. @property
  296. def groups(self):
  297. return self._groups
  298. @property
  299. def addresses(self):
  300. if self._addresses is None:
  301. self._addresses = tuple(address for group in self._groups
  302. for address in group.addresses)
  303. return self._addresses
  304. class UniqueAddressHeader(AddressHeader):
  305. max_count = 1
  306. class SingleAddressHeader(AddressHeader):
  307. @property
  308. def address(self):
  309. if len(self.addresses)!=1:
  310. raise ValueError(("value of single address header {} is not "
  311. "a single address").format(self.name))
  312. return self.addresses[0]
  313. class UniqueSingleAddressHeader(SingleAddressHeader):
  314. max_count = 1
  315. class MIMEVersionHeader:
  316. max_count = 1
  317. value_parser = staticmethod(parser.parse_mime_version)
  318. @classmethod
  319. def parse(cls, value, kwds):
  320. kwds['parse_tree'] = parse_tree = cls.value_parser(value)
  321. kwds['decoded'] = str(parse_tree)
  322. kwds['defects'].extend(parse_tree.all_defects)
  323. kwds['major'] = None if parse_tree.minor is None else parse_tree.major
  324. kwds['minor'] = parse_tree.minor
  325. if parse_tree.minor is not None:
  326. kwds['version'] = '{}.{}'.format(kwds['major'], kwds['minor'])
  327. else:
  328. kwds['version'] = None
  329. def init(self, *args, **kw):
  330. self._version = kw.pop('version')
  331. self._major = kw.pop('major')
  332. self._minor = kw.pop('minor')
  333. super().init(*args, **kw)
  334. @property
  335. def major(self):
  336. return self._major
  337. @property
  338. def minor(self):
  339. return self._minor
  340. @property
  341. def version(self):
  342. return self._version
  343. class ParameterizedMIMEHeader:
  344. # Mixin that handles the params dict. Must be subclassed and
  345. # a property value_parser for the specific header provided.
  346. max_count = 1
  347. @classmethod
  348. def parse(cls, value, kwds):
  349. kwds['parse_tree'] = parse_tree = cls.value_parser(value)
  350. kwds['decoded'] = str(parse_tree)
  351. kwds['defects'].extend(parse_tree.all_defects)
  352. if parse_tree.params is None:
  353. kwds['params'] = {}
  354. else:
  355. # The MIME RFCs specify that parameter ordering is arbitrary.
  356. kwds['params'] = {utils._sanitize(name).lower():
  357. utils._sanitize(value)
  358. for name, value in parse_tree.params}
  359. def init(self, *args, **kw):
  360. self._params = kw.pop('params')
  361. super().init(*args, **kw)
  362. @property
  363. def params(self):
  364. return MappingProxyType(self._params)
  365. class ContentTypeHeader(ParameterizedMIMEHeader):
  366. value_parser = staticmethod(parser.parse_content_type_header)
  367. def init(self, *args, **kw):
  368. super().init(*args, **kw)
  369. self._maintype = utils._sanitize(self._parse_tree.maintype)
  370. self._subtype = utils._sanitize(self._parse_tree.subtype)
  371. @property
  372. def maintype(self):
  373. return self._maintype
  374. @property
  375. def subtype(self):
  376. return self._subtype
  377. @property
  378. def content_type(self):
  379. return self.maintype + '/' + self.subtype
  380. class ContentDispositionHeader(ParameterizedMIMEHeader):
  381. value_parser = staticmethod(parser.parse_content_disposition_header)
  382. def init(self, *args, **kw):
  383. super().init(*args, **kw)
  384. cd = self._parse_tree.content_disposition
  385. self._content_disposition = cd if cd is None else utils._sanitize(cd)
  386. @property
  387. def content_disposition(self):
  388. return self._content_disposition
  389. class ContentTransferEncodingHeader:
  390. max_count = 1
  391. value_parser = staticmethod(parser.parse_content_transfer_encoding_header)
  392. @classmethod
  393. def parse(cls, value, kwds):
  394. kwds['parse_tree'] = parse_tree = cls.value_parser(value)
  395. kwds['decoded'] = str(parse_tree)
  396. kwds['defects'].extend(parse_tree.all_defects)
  397. def init(self, *args, **kw):
  398. super().init(*args, **kw)
  399. self._cte = utils._sanitize(self._parse_tree.cte)
  400. @property
  401. def cte(self):
  402. return self._cte
  403. class MessageIDHeader:
  404. max_count = 1
  405. value_parser = staticmethod(parser.parse_message_id)
  406. @classmethod
  407. def parse(cls, value, kwds):
  408. kwds['parse_tree'] = parse_tree = cls.value_parser(value)
  409. kwds['decoded'] = str(parse_tree)
  410. kwds['defects'].extend(parse_tree.all_defects)
  411. # The header factory #
  412. _default_header_map = {
  413. 'subject': UniqueUnstructuredHeader,
  414. 'date': UniqueDateHeader,
  415. 'resent-date': DateHeader,
  416. 'orig-date': UniqueDateHeader,
  417. 'sender': UniqueSingleAddressHeader,
  418. 'resent-sender': SingleAddressHeader,
  419. 'to': UniqueAddressHeader,
  420. 'resent-to': AddressHeader,
  421. 'cc': UniqueAddressHeader,
  422. 'resent-cc': AddressHeader,
  423. 'bcc': UniqueAddressHeader,
  424. 'resent-bcc': AddressHeader,
  425. 'from': UniqueAddressHeader,
  426. 'resent-from': AddressHeader,
  427. 'reply-to': UniqueAddressHeader,
  428. 'mime-version': MIMEVersionHeader,
  429. 'content-type': ContentTypeHeader,
  430. 'content-disposition': ContentDispositionHeader,
  431. 'content-transfer-encoding': ContentTransferEncodingHeader,
  432. 'message-id': MessageIDHeader,
  433. }
  434. class HeaderRegistry:
  435. """A header_factory and header registry."""
  436. def __init__(self, base_class=BaseHeader, default_class=UnstructuredHeader,
  437. use_default_map=True):
  438. """Create a header_factory that works with the Policy API.
  439. base_class is the class that will be the last class in the created
  440. header class's __bases__ list. default_class is the class that will be
  441. used if "name" (see __call__) does not appear in the registry.
  442. use_default_map controls whether or not the default mapping of names to
  443. specialized classes is copied in to the registry when the factory is
  444. created. The default is True.
  445. """
  446. self.registry = {}
  447. self.base_class = base_class
  448. self.default_class = default_class
  449. if use_default_map:
  450. self.registry.update(_default_header_map)
  451. def map_to_type(self, name, cls):
  452. """Register cls as the specialized class for handling "name" headers.
  453. """
  454. self.registry[name.lower()] = cls
  455. def __getitem__(self, name):
  456. cls = self.registry.get(name.lower(), self.default_class)
  457. return type('_'+cls.__name__, (cls, self.base_class), {})
  458. def __call__(self, name, value):
  459. """Create a header instance for header 'name' from 'value'.
  460. Creates a header instance by creating a specialized class for parsing
  461. and representing the specified header by combining the factory
  462. base_class with a specialized class from the registry or the
  463. default_class, and passing the name and value to the constructed
  464. class's constructor.
  465. """
  466. return self[name](name, value)