17db96d56Sopenharmony_ci"""Representing and manipulating email headers via custom objects. 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciThis module provides an implementation of the HeaderRegistry API. 47db96d56Sopenharmony_ciThe implementation is designed to flexibly follow RFC5322 rules. 57db96d56Sopenharmony_ci""" 67db96d56Sopenharmony_cifrom types import MappingProxyType 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_cifrom email import utils 97db96d56Sopenharmony_cifrom email import errors 107db96d56Sopenharmony_cifrom email import _header_value_parser as parser 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_ciclass Address: 137db96d56Sopenharmony_ci 147db96d56Sopenharmony_ci def __init__(self, display_name='', username='', domain='', addr_spec=None): 157db96d56Sopenharmony_ci """Create an object representing a full email address. 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ci An address can have a 'display_name', a 'username', and a 'domain'. In 187db96d56Sopenharmony_ci addition to specifying the username and domain separately, they may be 197db96d56Sopenharmony_ci specified together by using the addr_spec keyword *instead of* the 207db96d56Sopenharmony_ci username and domain keywords. If an addr_spec string is specified it 217db96d56Sopenharmony_ci must be properly quoted according to RFC 5322 rules; an error will be 227db96d56Sopenharmony_ci raised if it is not. 237db96d56Sopenharmony_ci 247db96d56Sopenharmony_ci An Address object has display_name, username, domain, and addr_spec 257db96d56Sopenharmony_ci attributes, all of which are read-only. The addr_spec and the string 267db96d56Sopenharmony_ci value of the object are both quoted according to RFC5322 rules, but 277db96d56Sopenharmony_ci without any Content Transfer Encoding. 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci """ 307db96d56Sopenharmony_ci 317db96d56Sopenharmony_ci inputs = ''.join(filter(None, (display_name, username, domain, addr_spec))) 327db96d56Sopenharmony_ci if '\r' in inputs or '\n' in inputs: 337db96d56Sopenharmony_ci raise ValueError("invalid arguments; address parts cannot contain CR or LF") 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ci # This clause with its potential 'raise' may only happen when an 367db96d56Sopenharmony_ci # application program creates an Address object using an addr_spec 377db96d56Sopenharmony_ci # keyword. The email library code itself must always supply username 387db96d56Sopenharmony_ci # and domain. 397db96d56Sopenharmony_ci if addr_spec is not None: 407db96d56Sopenharmony_ci if username or domain: 417db96d56Sopenharmony_ci raise TypeError("addrspec specified when username and/or " 427db96d56Sopenharmony_ci "domain also specified") 437db96d56Sopenharmony_ci a_s, rest = parser.get_addr_spec(addr_spec) 447db96d56Sopenharmony_ci if rest: 457db96d56Sopenharmony_ci raise ValueError("Invalid addr_spec; only '{}' " 467db96d56Sopenharmony_ci "could be parsed from '{}'".format( 477db96d56Sopenharmony_ci a_s, addr_spec)) 487db96d56Sopenharmony_ci if a_s.all_defects: 497db96d56Sopenharmony_ci raise a_s.all_defects[0] 507db96d56Sopenharmony_ci username = a_s.local_part 517db96d56Sopenharmony_ci domain = a_s.domain 527db96d56Sopenharmony_ci self._display_name = display_name 537db96d56Sopenharmony_ci self._username = username 547db96d56Sopenharmony_ci self._domain = domain 557db96d56Sopenharmony_ci 567db96d56Sopenharmony_ci @property 577db96d56Sopenharmony_ci def display_name(self): 587db96d56Sopenharmony_ci return self._display_name 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_ci @property 617db96d56Sopenharmony_ci def username(self): 627db96d56Sopenharmony_ci return self._username 637db96d56Sopenharmony_ci 647db96d56Sopenharmony_ci @property 657db96d56Sopenharmony_ci def domain(self): 667db96d56Sopenharmony_ci return self._domain 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ci @property 697db96d56Sopenharmony_ci def addr_spec(self): 707db96d56Sopenharmony_ci """The addr_spec (username@domain) portion of the address, quoted 717db96d56Sopenharmony_ci according to RFC 5322 rules, but with no Content Transfer Encoding. 727db96d56Sopenharmony_ci """ 737db96d56Sopenharmony_ci lp = self.username 747db96d56Sopenharmony_ci if not parser.DOT_ATOM_ENDS.isdisjoint(lp): 757db96d56Sopenharmony_ci lp = parser.quote_string(lp) 767db96d56Sopenharmony_ci if self.domain: 777db96d56Sopenharmony_ci return lp + '@' + self.domain 787db96d56Sopenharmony_ci if not lp: 797db96d56Sopenharmony_ci return '<>' 807db96d56Sopenharmony_ci return lp 817db96d56Sopenharmony_ci 827db96d56Sopenharmony_ci def __repr__(self): 837db96d56Sopenharmony_ci return "{}(display_name={!r}, username={!r}, domain={!r})".format( 847db96d56Sopenharmony_ci self.__class__.__name__, 857db96d56Sopenharmony_ci self.display_name, self.username, self.domain) 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ci def __str__(self): 887db96d56Sopenharmony_ci disp = self.display_name 897db96d56Sopenharmony_ci if not parser.SPECIALS.isdisjoint(disp): 907db96d56Sopenharmony_ci disp = parser.quote_string(disp) 917db96d56Sopenharmony_ci if disp: 927db96d56Sopenharmony_ci addr_spec = '' if self.addr_spec=='<>' else self.addr_spec 937db96d56Sopenharmony_ci return "{} <{}>".format(disp, addr_spec) 947db96d56Sopenharmony_ci return self.addr_spec 957db96d56Sopenharmony_ci 967db96d56Sopenharmony_ci def __eq__(self, other): 977db96d56Sopenharmony_ci if not isinstance(other, Address): 987db96d56Sopenharmony_ci return NotImplemented 997db96d56Sopenharmony_ci return (self.display_name == other.display_name and 1007db96d56Sopenharmony_ci self.username == other.username and 1017db96d56Sopenharmony_ci self.domain == other.domain) 1027db96d56Sopenharmony_ci 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ciclass Group: 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_ci def __init__(self, display_name=None, addresses=None): 1077db96d56Sopenharmony_ci """Create an object representing an address group. 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci An address group consists of a display_name followed by colon and a 1107db96d56Sopenharmony_ci list of addresses (see Address) terminated by a semi-colon. The Group 1117db96d56Sopenharmony_ci is created by specifying a display_name and a possibly empty list of 1127db96d56Sopenharmony_ci Address objects. A Group can also be used to represent a single 1137db96d56Sopenharmony_ci address that is not in a group, which is convenient when manipulating 1147db96d56Sopenharmony_ci lists that are a combination of Groups and individual Addresses. In 1157db96d56Sopenharmony_ci this case the display_name should be set to None. In particular, the 1167db96d56Sopenharmony_ci string representation of a Group whose display_name is None is the same 1177db96d56Sopenharmony_ci as the Address object, if there is one and only one Address object in 1187db96d56Sopenharmony_ci the addresses list. 1197db96d56Sopenharmony_ci 1207db96d56Sopenharmony_ci """ 1217db96d56Sopenharmony_ci self._display_name = display_name 1227db96d56Sopenharmony_ci self._addresses = tuple(addresses) if addresses else tuple() 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_ci @property 1257db96d56Sopenharmony_ci def display_name(self): 1267db96d56Sopenharmony_ci return self._display_name 1277db96d56Sopenharmony_ci 1287db96d56Sopenharmony_ci @property 1297db96d56Sopenharmony_ci def addresses(self): 1307db96d56Sopenharmony_ci return self._addresses 1317db96d56Sopenharmony_ci 1327db96d56Sopenharmony_ci def __repr__(self): 1337db96d56Sopenharmony_ci return "{}(display_name={!r}, addresses={!r}".format( 1347db96d56Sopenharmony_ci self.__class__.__name__, 1357db96d56Sopenharmony_ci self.display_name, self.addresses) 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci def __str__(self): 1387db96d56Sopenharmony_ci if self.display_name is None and len(self.addresses)==1: 1397db96d56Sopenharmony_ci return str(self.addresses[0]) 1407db96d56Sopenharmony_ci disp = self.display_name 1417db96d56Sopenharmony_ci if disp is not None and not parser.SPECIALS.isdisjoint(disp): 1427db96d56Sopenharmony_ci disp = parser.quote_string(disp) 1437db96d56Sopenharmony_ci adrstr = ", ".join(str(x) for x in self.addresses) 1447db96d56Sopenharmony_ci adrstr = ' ' + adrstr if adrstr else adrstr 1457db96d56Sopenharmony_ci return "{}:{};".format(disp, adrstr) 1467db96d56Sopenharmony_ci 1477db96d56Sopenharmony_ci def __eq__(self, other): 1487db96d56Sopenharmony_ci if not isinstance(other, Group): 1497db96d56Sopenharmony_ci return NotImplemented 1507db96d56Sopenharmony_ci return (self.display_name == other.display_name and 1517db96d56Sopenharmony_ci self.addresses == other.addresses) 1527db96d56Sopenharmony_ci 1537db96d56Sopenharmony_ci 1547db96d56Sopenharmony_ci# Header Classes # 1557db96d56Sopenharmony_ci 1567db96d56Sopenharmony_ciclass BaseHeader(str): 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_ci """Base class for message headers. 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci Implements generic behavior and provides tools for subclasses. 1617db96d56Sopenharmony_ci 1627db96d56Sopenharmony_ci A subclass must define a classmethod named 'parse' that takes an unfolded 1637db96d56Sopenharmony_ci value string and a dictionary as its arguments. The dictionary will 1647db96d56Sopenharmony_ci contain one key, 'defects', initialized to an empty list. After the call 1657db96d56Sopenharmony_ci the dictionary must contain two additional keys: parse_tree, set to the 1667db96d56Sopenharmony_ci parse tree obtained from parsing the header, and 'decoded', set to the 1677db96d56Sopenharmony_ci string value of the idealized representation of the data from the value. 1687db96d56Sopenharmony_ci (That is, encoded words are decoded, and values that have canonical 1697db96d56Sopenharmony_ci representations are so represented.) 1707db96d56Sopenharmony_ci 1717db96d56Sopenharmony_ci The defects key is intended to collect parsing defects, which the message 1727db96d56Sopenharmony_ci parser will subsequently dispose of as appropriate. The parser should not, 1737db96d56Sopenharmony_ci insofar as practical, raise any errors. Defects should be added to the 1747db96d56Sopenharmony_ci list instead. The standard header parsers register defects for RFC 1757db96d56Sopenharmony_ci compliance issues, for obsolete RFC syntax, and for unrecoverable parsing 1767db96d56Sopenharmony_ci errors. 1777db96d56Sopenharmony_ci 1787db96d56Sopenharmony_ci The parse method may add additional keys to the dictionary. In this case 1797db96d56Sopenharmony_ci the subclass must define an 'init' method, which will be passed the 1807db96d56Sopenharmony_ci dictionary as its keyword arguments. The method should use (usually by 1817db96d56Sopenharmony_ci setting them as the value of similarly named attributes) and remove all the 1827db96d56Sopenharmony_ci extra keys added by its parse method, and then use super to call its parent 1837db96d56Sopenharmony_ci class with the remaining arguments and keywords. 1847db96d56Sopenharmony_ci 1857db96d56Sopenharmony_ci The subclass should also make sure that a 'max_count' attribute is defined 1867db96d56Sopenharmony_ci that is either None or 1. XXX: need to better define this API. 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci """ 1897db96d56Sopenharmony_ci 1907db96d56Sopenharmony_ci def __new__(cls, name, value): 1917db96d56Sopenharmony_ci kwds = {'defects': []} 1927db96d56Sopenharmony_ci cls.parse(value, kwds) 1937db96d56Sopenharmony_ci if utils._has_surrogates(kwds['decoded']): 1947db96d56Sopenharmony_ci kwds['decoded'] = utils._sanitize(kwds['decoded']) 1957db96d56Sopenharmony_ci self = str.__new__(cls, kwds['decoded']) 1967db96d56Sopenharmony_ci del kwds['decoded'] 1977db96d56Sopenharmony_ci self.init(name, **kwds) 1987db96d56Sopenharmony_ci return self 1997db96d56Sopenharmony_ci 2007db96d56Sopenharmony_ci def init(self, name, *, parse_tree, defects): 2017db96d56Sopenharmony_ci self._name = name 2027db96d56Sopenharmony_ci self._parse_tree = parse_tree 2037db96d56Sopenharmony_ci self._defects = defects 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_ci @property 2067db96d56Sopenharmony_ci def name(self): 2077db96d56Sopenharmony_ci return self._name 2087db96d56Sopenharmony_ci 2097db96d56Sopenharmony_ci @property 2107db96d56Sopenharmony_ci def defects(self): 2117db96d56Sopenharmony_ci return tuple(self._defects) 2127db96d56Sopenharmony_ci 2137db96d56Sopenharmony_ci def __reduce__(self): 2147db96d56Sopenharmony_ci return ( 2157db96d56Sopenharmony_ci _reconstruct_header, 2167db96d56Sopenharmony_ci ( 2177db96d56Sopenharmony_ci self.__class__.__name__, 2187db96d56Sopenharmony_ci self.__class__.__bases__, 2197db96d56Sopenharmony_ci str(self), 2207db96d56Sopenharmony_ci ), 2217db96d56Sopenharmony_ci self.__getstate__()) 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_ci @classmethod 2247db96d56Sopenharmony_ci def _reconstruct(cls, value): 2257db96d56Sopenharmony_ci return str.__new__(cls, value) 2267db96d56Sopenharmony_ci 2277db96d56Sopenharmony_ci def fold(self, *, policy): 2287db96d56Sopenharmony_ci """Fold header according to policy. 2297db96d56Sopenharmony_ci 2307db96d56Sopenharmony_ci The parsed representation of the header is folded according to 2317db96d56Sopenharmony_ci RFC5322 rules, as modified by the policy. If the parse tree 2327db96d56Sopenharmony_ci contains surrogateescaped bytes, the bytes are CTE encoded using 2337db96d56Sopenharmony_ci the charset 'unknown-8bit". 2347db96d56Sopenharmony_ci 2357db96d56Sopenharmony_ci Any non-ASCII characters in the parse tree are CTE encoded using 2367db96d56Sopenharmony_ci charset utf-8. XXX: make this a policy setting. 2377db96d56Sopenharmony_ci 2387db96d56Sopenharmony_ci The returned value is an ASCII-only string possibly containing linesep 2397db96d56Sopenharmony_ci characters, and ending with a linesep character. The string includes 2407db96d56Sopenharmony_ci the header name and the ': ' separator. 2417db96d56Sopenharmony_ci 2427db96d56Sopenharmony_ci """ 2437db96d56Sopenharmony_ci # At some point we need to put fws here if it was in the source. 2447db96d56Sopenharmony_ci header = parser.Header([ 2457db96d56Sopenharmony_ci parser.HeaderLabel([ 2467db96d56Sopenharmony_ci parser.ValueTerminal(self.name, 'header-name'), 2477db96d56Sopenharmony_ci parser.ValueTerminal(':', 'header-sep')]), 2487db96d56Sopenharmony_ci ]) 2497db96d56Sopenharmony_ci if self._parse_tree: 2507db96d56Sopenharmony_ci header.append( 2517db96d56Sopenharmony_ci parser.CFWSList([parser.WhiteSpaceTerminal(' ', 'fws')])) 2527db96d56Sopenharmony_ci header.append(self._parse_tree) 2537db96d56Sopenharmony_ci return header.fold(policy=policy) 2547db96d56Sopenharmony_ci 2557db96d56Sopenharmony_ci 2567db96d56Sopenharmony_cidef _reconstruct_header(cls_name, bases, value): 2577db96d56Sopenharmony_ci return type(cls_name, bases, {})._reconstruct(value) 2587db96d56Sopenharmony_ci 2597db96d56Sopenharmony_ci 2607db96d56Sopenharmony_ciclass UnstructuredHeader: 2617db96d56Sopenharmony_ci 2627db96d56Sopenharmony_ci max_count = None 2637db96d56Sopenharmony_ci value_parser = staticmethod(parser.get_unstructured) 2647db96d56Sopenharmony_ci 2657db96d56Sopenharmony_ci @classmethod 2667db96d56Sopenharmony_ci def parse(cls, value, kwds): 2677db96d56Sopenharmony_ci kwds['parse_tree'] = cls.value_parser(value) 2687db96d56Sopenharmony_ci kwds['decoded'] = str(kwds['parse_tree']) 2697db96d56Sopenharmony_ci 2707db96d56Sopenharmony_ci 2717db96d56Sopenharmony_ciclass UniqueUnstructuredHeader(UnstructuredHeader): 2727db96d56Sopenharmony_ci 2737db96d56Sopenharmony_ci max_count = 1 2747db96d56Sopenharmony_ci 2757db96d56Sopenharmony_ci 2767db96d56Sopenharmony_ciclass DateHeader: 2777db96d56Sopenharmony_ci 2787db96d56Sopenharmony_ci """Header whose value consists of a single timestamp. 2797db96d56Sopenharmony_ci 2807db96d56Sopenharmony_ci Provides an additional attribute, datetime, which is either an aware 2817db96d56Sopenharmony_ci datetime using a timezone, or a naive datetime if the timezone 2827db96d56Sopenharmony_ci in the input string is -0000. Also accepts a datetime as input. 2837db96d56Sopenharmony_ci The 'value' attribute is the normalized form of the timestamp, 2847db96d56Sopenharmony_ci which means it is the output of format_datetime on the datetime. 2857db96d56Sopenharmony_ci """ 2867db96d56Sopenharmony_ci 2877db96d56Sopenharmony_ci max_count = None 2887db96d56Sopenharmony_ci 2897db96d56Sopenharmony_ci # This is used only for folding, not for creating 'decoded'. 2907db96d56Sopenharmony_ci value_parser = staticmethod(parser.get_unstructured) 2917db96d56Sopenharmony_ci 2927db96d56Sopenharmony_ci @classmethod 2937db96d56Sopenharmony_ci def parse(cls, value, kwds): 2947db96d56Sopenharmony_ci if not value: 2957db96d56Sopenharmony_ci kwds['defects'].append(errors.HeaderMissingRequiredValue()) 2967db96d56Sopenharmony_ci kwds['datetime'] = None 2977db96d56Sopenharmony_ci kwds['decoded'] = '' 2987db96d56Sopenharmony_ci kwds['parse_tree'] = parser.TokenList() 2997db96d56Sopenharmony_ci return 3007db96d56Sopenharmony_ci if isinstance(value, str): 3017db96d56Sopenharmony_ci kwds['decoded'] = value 3027db96d56Sopenharmony_ci try: 3037db96d56Sopenharmony_ci value = utils.parsedate_to_datetime(value) 3047db96d56Sopenharmony_ci except ValueError: 3057db96d56Sopenharmony_ci kwds['defects'].append(errors.InvalidDateDefect('Invalid date value or format')) 3067db96d56Sopenharmony_ci kwds['datetime'] = None 3077db96d56Sopenharmony_ci kwds['parse_tree'] = parser.TokenList() 3087db96d56Sopenharmony_ci return 3097db96d56Sopenharmony_ci kwds['datetime'] = value 3107db96d56Sopenharmony_ci kwds['decoded'] = utils.format_datetime(kwds['datetime']) 3117db96d56Sopenharmony_ci kwds['parse_tree'] = cls.value_parser(kwds['decoded']) 3127db96d56Sopenharmony_ci 3137db96d56Sopenharmony_ci def init(self, *args, **kw): 3147db96d56Sopenharmony_ci self._datetime = kw.pop('datetime') 3157db96d56Sopenharmony_ci super().init(*args, **kw) 3167db96d56Sopenharmony_ci 3177db96d56Sopenharmony_ci @property 3187db96d56Sopenharmony_ci def datetime(self): 3197db96d56Sopenharmony_ci return self._datetime 3207db96d56Sopenharmony_ci 3217db96d56Sopenharmony_ci 3227db96d56Sopenharmony_ciclass UniqueDateHeader(DateHeader): 3237db96d56Sopenharmony_ci 3247db96d56Sopenharmony_ci max_count = 1 3257db96d56Sopenharmony_ci 3267db96d56Sopenharmony_ci 3277db96d56Sopenharmony_ciclass AddressHeader: 3287db96d56Sopenharmony_ci 3297db96d56Sopenharmony_ci max_count = None 3307db96d56Sopenharmony_ci 3317db96d56Sopenharmony_ci @staticmethod 3327db96d56Sopenharmony_ci def value_parser(value): 3337db96d56Sopenharmony_ci address_list, value = parser.get_address_list(value) 3347db96d56Sopenharmony_ci assert not value, 'this should not happen' 3357db96d56Sopenharmony_ci return address_list 3367db96d56Sopenharmony_ci 3377db96d56Sopenharmony_ci @classmethod 3387db96d56Sopenharmony_ci def parse(cls, value, kwds): 3397db96d56Sopenharmony_ci if isinstance(value, str): 3407db96d56Sopenharmony_ci # We are translating here from the RFC language (address/mailbox) 3417db96d56Sopenharmony_ci # to our API language (group/address). 3427db96d56Sopenharmony_ci kwds['parse_tree'] = address_list = cls.value_parser(value) 3437db96d56Sopenharmony_ci groups = [] 3447db96d56Sopenharmony_ci for addr in address_list.addresses: 3457db96d56Sopenharmony_ci groups.append(Group(addr.display_name, 3467db96d56Sopenharmony_ci [Address(mb.display_name or '', 3477db96d56Sopenharmony_ci mb.local_part or '', 3487db96d56Sopenharmony_ci mb.domain or '') 3497db96d56Sopenharmony_ci for mb in addr.all_mailboxes])) 3507db96d56Sopenharmony_ci defects = list(address_list.all_defects) 3517db96d56Sopenharmony_ci else: 3527db96d56Sopenharmony_ci # Assume it is Address/Group stuff 3537db96d56Sopenharmony_ci if not hasattr(value, '__iter__'): 3547db96d56Sopenharmony_ci value = [value] 3557db96d56Sopenharmony_ci groups = [Group(None, [item]) if not hasattr(item, 'addresses') 3567db96d56Sopenharmony_ci else item 3577db96d56Sopenharmony_ci for item in value] 3587db96d56Sopenharmony_ci defects = [] 3597db96d56Sopenharmony_ci kwds['groups'] = groups 3607db96d56Sopenharmony_ci kwds['defects'] = defects 3617db96d56Sopenharmony_ci kwds['decoded'] = ', '.join([str(item) for item in groups]) 3627db96d56Sopenharmony_ci if 'parse_tree' not in kwds: 3637db96d56Sopenharmony_ci kwds['parse_tree'] = cls.value_parser(kwds['decoded']) 3647db96d56Sopenharmony_ci 3657db96d56Sopenharmony_ci def init(self, *args, **kw): 3667db96d56Sopenharmony_ci self._groups = tuple(kw.pop('groups')) 3677db96d56Sopenharmony_ci self._addresses = None 3687db96d56Sopenharmony_ci super().init(*args, **kw) 3697db96d56Sopenharmony_ci 3707db96d56Sopenharmony_ci @property 3717db96d56Sopenharmony_ci def groups(self): 3727db96d56Sopenharmony_ci return self._groups 3737db96d56Sopenharmony_ci 3747db96d56Sopenharmony_ci @property 3757db96d56Sopenharmony_ci def addresses(self): 3767db96d56Sopenharmony_ci if self._addresses is None: 3777db96d56Sopenharmony_ci self._addresses = tuple(address for group in self._groups 3787db96d56Sopenharmony_ci for address in group.addresses) 3797db96d56Sopenharmony_ci return self._addresses 3807db96d56Sopenharmony_ci 3817db96d56Sopenharmony_ci 3827db96d56Sopenharmony_ciclass UniqueAddressHeader(AddressHeader): 3837db96d56Sopenharmony_ci 3847db96d56Sopenharmony_ci max_count = 1 3857db96d56Sopenharmony_ci 3867db96d56Sopenharmony_ci 3877db96d56Sopenharmony_ciclass SingleAddressHeader(AddressHeader): 3887db96d56Sopenharmony_ci 3897db96d56Sopenharmony_ci @property 3907db96d56Sopenharmony_ci def address(self): 3917db96d56Sopenharmony_ci if len(self.addresses)!=1: 3927db96d56Sopenharmony_ci raise ValueError(("value of single address header {} is not " 3937db96d56Sopenharmony_ci "a single address").format(self.name)) 3947db96d56Sopenharmony_ci return self.addresses[0] 3957db96d56Sopenharmony_ci 3967db96d56Sopenharmony_ci 3977db96d56Sopenharmony_ciclass UniqueSingleAddressHeader(SingleAddressHeader): 3987db96d56Sopenharmony_ci 3997db96d56Sopenharmony_ci max_count = 1 4007db96d56Sopenharmony_ci 4017db96d56Sopenharmony_ci 4027db96d56Sopenharmony_ciclass MIMEVersionHeader: 4037db96d56Sopenharmony_ci 4047db96d56Sopenharmony_ci max_count = 1 4057db96d56Sopenharmony_ci 4067db96d56Sopenharmony_ci value_parser = staticmethod(parser.parse_mime_version) 4077db96d56Sopenharmony_ci 4087db96d56Sopenharmony_ci @classmethod 4097db96d56Sopenharmony_ci def parse(cls, value, kwds): 4107db96d56Sopenharmony_ci kwds['parse_tree'] = parse_tree = cls.value_parser(value) 4117db96d56Sopenharmony_ci kwds['decoded'] = str(parse_tree) 4127db96d56Sopenharmony_ci kwds['defects'].extend(parse_tree.all_defects) 4137db96d56Sopenharmony_ci kwds['major'] = None if parse_tree.minor is None else parse_tree.major 4147db96d56Sopenharmony_ci kwds['minor'] = parse_tree.minor 4157db96d56Sopenharmony_ci if parse_tree.minor is not None: 4167db96d56Sopenharmony_ci kwds['version'] = '{}.{}'.format(kwds['major'], kwds['minor']) 4177db96d56Sopenharmony_ci else: 4187db96d56Sopenharmony_ci kwds['version'] = None 4197db96d56Sopenharmony_ci 4207db96d56Sopenharmony_ci def init(self, *args, **kw): 4217db96d56Sopenharmony_ci self._version = kw.pop('version') 4227db96d56Sopenharmony_ci self._major = kw.pop('major') 4237db96d56Sopenharmony_ci self._minor = kw.pop('minor') 4247db96d56Sopenharmony_ci super().init(*args, **kw) 4257db96d56Sopenharmony_ci 4267db96d56Sopenharmony_ci @property 4277db96d56Sopenharmony_ci def major(self): 4287db96d56Sopenharmony_ci return self._major 4297db96d56Sopenharmony_ci 4307db96d56Sopenharmony_ci @property 4317db96d56Sopenharmony_ci def minor(self): 4327db96d56Sopenharmony_ci return self._minor 4337db96d56Sopenharmony_ci 4347db96d56Sopenharmony_ci @property 4357db96d56Sopenharmony_ci def version(self): 4367db96d56Sopenharmony_ci return self._version 4377db96d56Sopenharmony_ci 4387db96d56Sopenharmony_ci 4397db96d56Sopenharmony_ciclass ParameterizedMIMEHeader: 4407db96d56Sopenharmony_ci 4417db96d56Sopenharmony_ci # Mixin that handles the params dict. Must be subclassed and 4427db96d56Sopenharmony_ci # a property value_parser for the specific header provided. 4437db96d56Sopenharmony_ci 4447db96d56Sopenharmony_ci max_count = 1 4457db96d56Sopenharmony_ci 4467db96d56Sopenharmony_ci @classmethod 4477db96d56Sopenharmony_ci def parse(cls, value, kwds): 4487db96d56Sopenharmony_ci kwds['parse_tree'] = parse_tree = cls.value_parser(value) 4497db96d56Sopenharmony_ci kwds['decoded'] = str(parse_tree) 4507db96d56Sopenharmony_ci kwds['defects'].extend(parse_tree.all_defects) 4517db96d56Sopenharmony_ci if parse_tree.params is None: 4527db96d56Sopenharmony_ci kwds['params'] = {} 4537db96d56Sopenharmony_ci else: 4547db96d56Sopenharmony_ci # The MIME RFCs specify that parameter ordering is arbitrary. 4557db96d56Sopenharmony_ci kwds['params'] = {utils._sanitize(name).lower(): 4567db96d56Sopenharmony_ci utils._sanitize(value) 4577db96d56Sopenharmony_ci for name, value in parse_tree.params} 4587db96d56Sopenharmony_ci 4597db96d56Sopenharmony_ci def init(self, *args, **kw): 4607db96d56Sopenharmony_ci self._params = kw.pop('params') 4617db96d56Sopenharmony_ci super().init(*args, **kw) 4627db96d56Sopenharmony_ci 4637db96d56Sopenharmony_ci @property 4647db96d56Sopenharmony_ci def params(self): 4657db96d56Sopenharmony_ci return MappingProxyType(self._params) 4667db96d56Sopenharmony_ci 4677db96d56Sopenharmony_ci 4687db96d56Sopenharmony_ciclass ContentTypeHeader(ParameterizedMIMEHeader): 4697db96d56Sopenharmony_ci 4707db96d56Sopenharmony_ci value_parser = staticmethod(parser.parse_content_type_header) 4717db96d56Sopenharmony_ci 4727db96d56Sopenharmony_ci def init(self, *args, **kw): 4737db96d56Sopenharmony_ci super().init(*args, **kw) 4747db96d56Sopenharmony_ci self._maintype = utils._sanitize(self._parse_tree.maintype) 4757db96d56Sopenharmony_ci self._subtype = utils._sanitize(self._parse_tree.subtype) 4767db96d56Sopenharmony_ci 4777db96d56Sopenharmony_ci @property 4787db96d56Sopenharmony_ci def maintype(self): 4797db96d56Sopenharmony_ci return self._maintype 4807db96d56Sopenharmony_ci 4817db96d56Sopenharmony_ci @property 4827db96d56Sopenharmony_ci def subtype(self): 4837db96d56Sopenharmony_ci return self._subtype 4847db96d56Sopenharmony_ci 4857db96d56Sopenharmony_ci @property 4867db96d56Sopenharmony_ci def content_type(self): 4877db96d56Sopenharmony_ci return self.maintype + '/' + self.subtype 4887db96d56Sopenharmony_ci 4897db96d56Sopenharmony_ci 4907db96d56Sopenharmony_ciclass ContentDispositionHeader(ParameterizedMIMEHeader): 4917db96d56Sopenharmony_ci 4927db96d56Sopenharmony_ci value_parser = staticmethod(parser.parse_content_disposition_header) 4937db96d56Sopenharmony_ci 4947db96d56Sopenharmony_ci def init(self, *args, **kw): 4957db96d56Sopenharmony_ci super().init(*args, **kw) 4967db96d56Sopenharmony_ci cd = self._parse_tree.content_disposition 4977db96d56Sopenharmony_ci self._content_disposition = cd if cd is None else utils._sanitize(cd) 4987db96d56Sopenharmony_ci 4997db96d56Sopenharmony_ci @property 5007db96d56Sopenharmony_ci def content_disposition(self): 5017db96d56Sopenharmony_ci return self._content_disposition 5027db96d56Sopenharmony_ci 5037db96d56Sopenharmony_ci 5047db96d56Sopenharmony_ciclass ContentTransferEncodingHeader: 5057db96d56Sopenharmony_ci 5067db96d56Sopenharmony_ci max_count = 1 5077db96d56Sopenharmony_ci 5087db96d56Sopenharmony_ci value_parser = staticmethod(parser.parse_content_transfer_encoding_header) 5097db96d56Sopenharmony_ci 5107db96d56Sopenharmony_ci @classmethod 5117db96d56Sopenharmony_ci def parse(cls, value, kwds): 5127db96d56Sopenharmony_ci kwds['parse_tree'] = parse_tree = cls.value_parser(value) 5137db96d56Sopenharmony_ci kwds['decoded'] = str(parse_tree) 5147db96d56Sopenharmony_ci kwds['defects'].extend(parse_tree.all_defects) 5157db96d56Sopenharmony_ci 5167db96d56Sopenharmony_ci def init(self, *args, **kw): 5177db96d56Sopenharmony_ci super().init(*args, **kw) 5187db96d56Sopenharmony_ci self._cte = utils._sanitize(self._parse_tree.cte) 5197db96d56Sopenharmony_ci 5207db96d56Sopenharmony_ci @property 5217db96d56Sopenharmony_ci def cte(self): 5227db96d56Sopenharmony_ci return self._cte 5237db96d56Sopenharmony_ci 5247db96d56Sopenharmony_ci 5257db96d56Sopenharmony_ciclass MessageIDHeader: 5267db96d56Sopenharmony_ci 5277db96d56Sopenharmony_ci max_count = 1 5287db96d56Sopenharmony_ci value_parser = staticmethod(parser.parse_message_id) 5297db96d56Sopenharmony_ci 5307db96d56Sopenharmony_ci @classmethod 5317db96d56Sopenharmony_ci def parse(cls, value, kwds): 5327db96d56Sopenharmony_ci kwds['parse_tree'] = parse_tree = cls.value_parser(value) 5337db96d56Sopenharmony_ci kwds['decoded'] = str(parse_tree) 5347db96d56Sopenharmony_ci kwds['defects'].extend(parse_tree.all_defects) 5357db96d56Sopenharmony_ci 5367db96d56Sopenharmony_ci 5377db96d56Sopenharmony_ci# The header factory # 5387db96d56Sopenharmony_ci 5397db96d56Sopenharmony_ci_default_header_map = { 5407db96d56Sopenharmony_ci 'subject': UniqueUnstructuredHeader, 5417db96d56Sopenharmony_ci 'date': UniqueDateHeader, 5427db96d56Sopenharmony_ci 'resent-date': DateHeader, 5437db96d56Sopenharmony_ci 'orig-date': UniqueDateHeader, 5447db96d56Sopenharmony_ci 'sender': UniqueSingleAddressHeader, 5457db96d56Sopenharmony_ci 'resent-sender': SingleAddressHeader, 5467db96d56Sopenharmony_ci 'to': UniqueAddressHeader, 5477db96d56Sopenharmony_ci 'resent-to': AddressHeader, 5487db96d56Sopenharmony_ci 'cc': UniqueAddressHeader, 5497db96d56Sopenharmony_ci 'resent-cc': AddressHeader, 5507db96d56Sopenharmony_ci 'bcc': UniqueAddressHeader, 5517db96d56Sopenharmony_ci 'resent-bcc': AddressHeader, 5527db96d56Sopenharmony_ci 'from': UniqueAddressHeader, 5537db96d56Sopenharmony_ci 'resent-from': AddressHeader, 5547db96d56Sopenharmony_ci 'reply-to': UniqueAddressHeader, 5557db96d56Sopenharmony_ci 'mime-version': MIMEVersionHeader, 5567db96d56Sopenharmony_ci 'content-type': ContentTypeHeader, 5577db96d56Sopenharmony_ci 'content-disposition': ContentDispositionHeader, 5587db96d56Sopenharmony_ci 'content-transfer-encoding': ContentTransferEncodingHeader, 5597db96d56Sopenharmony_ci 'message-id': MessageIDHeader, 5607db96d56Sopenharmony_ci } 5617db96d56Sopenharmony_ci 5627db96d56Sopenharmony_ciclass HeaderRegistry: 5637db96d56Sopenharmony_ci 5647db96d56Sopenharmony_ci """A header_factory and header registry.""" 5657db96d56Sopenharmony_ci 5667db96d56Sopenharmony_ci def __init__(self, base_class=BaseHeader, default_class=UnstructuredHeader, 5677db96d56Sopenharmony_ci use_default_map=True): 5687db96d56Sopenharmony_ci """Create a header_factory that works with the Policy API. 5697db96d56Sopenharmony_ci 5707db96d56Sopenharmony_ci base_class is the class that will be the last class in the created 5717db96d56Sopenharmony_ci header class's __bases__ list. default_class is the class that will be 5727db96d56Sopenharmony_ci used if "name" (see __call__) does not appear in the registry. 5737db96d56Sopenharmony_ci use_default_map controls whether or not the default mapping of names to 5747db96d56Sopenharmony_ci specialized classes is copied in to the registry when the factory is 5757db96d56Sopenharmony_ci created. The default is True. 5767db96d56Sopenharmony_ci 5777db96d56Sopenharmony_ci """ 5787db96d56Sopenharmony_ci self.registry = {} 5797db96d56Sopenharmony_ci self.base_class = base_class 5807db96d56Sopenharmony_ci self.default_class = default_class 5817db96d56Sopenharmony_ci if use_default_map: 5827db96d56Sopenharmony_ci self.registry.update(_default_header_map) 5837db96d56Sopenharmony_ci 5847db96d56Sopenharmony_ci def map_to_type(self, name, cls): 5857db96d56Sopenharmony_ci """Register cls as the specialized class for handling "name" headers. 5867db96d56Sopenharmony_ci 5877db96d56Sopenharmony_ci """ 5887db96d56Sopenharmony_ci self.registry[name.lower()] = cls 5897db96d56Sopenharmony_ci 5907db96d56Sopenharmony_ci def __getitem__(self, name): 5917db96d56Sopenharmony_ci cls = self.registry.get(name.lower(), self.default_class) 5927db96d56Sopenharmony_ci return type('_'+cls.__name__, (cls, self.base_class), {}) 5937db96d56Sopenharmony_ci 5947db96d56Sopenharmony_ci def __call__(self, name, value): 5957db96d56Sopenharmony_ci """Create a header instance for header 'name' from 'value'. 5967db96d56Sopenharmony_ci 5977db96d56Sopenharmony_ci Creates a header instance by creating a specialized class for parsing 5987db96d56Sopenharmony_ci and representing the specified header by combining the factory 5997db96d56Sopenharmony_ci base_class with a specialized class from the registry or the 6007db96d56Sopenharmony_ci default_class, and passing the name and value to the constructed 6017db96d56Sopenharmony_ci class's constructor. 6027db96d56Sopenharmony_ci 6037db96d56Sopenharmony_ci """ 6047db96d56Sopenharmony_ci return self[name](name, value) 605