jpayne@68: """Policy framework for the email package. jpayne@68: jpayne@68: Allows fine grained feature control of how the package parses and emits data. jpayne@68: """ jpayne@68: jpayne@68: import abc jpayne@68: from email import header jpayne@68: from email import charset as _charset jpayne@68: from email.utils import _has_surrogates jpayne@68: jpayne@68: __all__ = [ jpayne@68: 'Policy', jpayne@68: 'Compat32', jpayne@68: 'compat32', jpayne@68: ] jpayne@68: jpayne@68: jpayne@68: class _PolicyBase: jpayne@68: jpayne@68: """Policy Object basic framework. jpayne@68: jpayne@68: This class is useless unless subclassed. A subclass should define jpayne@68: class attributes with defaults for any values that are to be jpayne@68: managed by the Policy object. The constructor will then allow jpayne@68: non-default values to be set for these attributes at instance jpayne@68: creation time. The instance will be callable, taking these same jpayne@68: attributes keyword arguments, and returning a new instance jpayne@68: identical to the called instance except for those values changed jpayne@68: by the keyword arguments. Instances may be added, yielding new jpayne@68: instances with any non-default values from the right hand jpayne@68: operand overriding those in the left hand operand. That is, jpayne@68: jpayne@68: A + B == A() jpayne@68: jpayne@68: The repr of an instance can be used to reconstruct the object jpayne@68: if and only if the repr of the values can be used to reconstruct jpayne@68: those values. jpayne@68: jpayne@68: """ jpayne@68: jpayne@68: def __init__(self, **kw): jpayne@68: """Create new Policy, possibly overriding some defaults. jpayne@68: jpayne@68: See class docstring for a list of overridable attributes. jpayne@68: jpayne@68: """ jpayne@68: for name, value in kw.items(): jpayne@68: if hasattr(self, name): jpayne@68: super(_PolicyBase,self).__setattr__(name, value) jpayne@68: else: jpayne@68: raise TypeError( jpayne@68: "{!r} is an invalid keyword argument for {}".format( jpayne@68: name, self.__class__.__name__)) jpayne@68: jpayne@68: def __repr__(self): jpayne@68: args = [ "{}={!r}".format(name, value) jpayne@68: for name, value in self.__dict__.items() ] jpayne@68: return "{}({})".format(self.__class__.__name__, ', '.join(args)) jpayne@68: jpayne@68: def clone(self, **kw): jpayne@68: """Return a new instance with specified attributes changed. jpayne@68: jpayne@68: The new instance has the same attribute values as the current object, jpayne@68: except for the changes passed in as keyword arguments. jpayne@68: jpayne@68: """ jpayne@68: newpolicy = self.__class__.__new__(self.__class__) jpayne@68: for attr, value in self.__dict__.items(): jpayne@68: object.__setattr__(newpolicy, attr, value) jpayne@68: for attr, value in kw.items(): jpayne@68: if not hasattr(self, attr): jpayne@68: raise TypeError( jpayne@68: "{!r} is an invalid keyword argument for {}".format( jpayne@68: attr, self.__class__.__name__)) jpayne@68: object.__setattr__(newpolicy, attr, value) jpayne@68: return newpolicy jpayne@68: jpayne@68: def __setattr__(self, name, value): jpayne@68: if hasattr(self, name): jpayne@68: msg = "{!r} object attribute {!r} is read-only" jpayne@68: else: jpayne@68: msg = "{!r} object has no attribute {!r}" jpayne@68: raise AttributeError(msg.format(self.__class__.__name__, name)) jpayne@68: jpayne@68: def __add__(self, other): jpayne@68: """Non-default values from right operand override those from left. jpayne@68: jpayne@68: The object returned is a new instance of the subclass. jpayne@68: jpayne@68: """ jpayne@68: return self.clone(**other.__dict__) jpayne@68: jpayne@68: jpayne@68: def _append_doc(doc, added_doc): jpayne@68: doc = doc.rsplit('\n', 1)[0] jpayne@68: added_doc = added_doc.split('\n', 1)[1] jpayne@68: return doc + '\n' + added_doc jpayne@68: jpayne@68: def _extend_docstrings(cls): jpayne@68: if cls.__doc__ and cls.__doc__.startswith('+'): jpayne@68: cls.__doc__ = _append_doc(cls.__bases__[0].__doc__, cls.__doc__) jpayne@68: for name, attr in cls.__dict__.items(): jpayne@68: if attr.__doc__ and attr.__doc__.startswith('+'): jpayne@68: for c in (c for base in cls.__bases__ for c in base.mro()): jpayne@68: doc = getattr(getattr(c, name), '__doc__') jpayne@68: if doc: jpayne@68: attr.__doc__ = _append_doc(doc, attr.__doc__) jpayne@68: break jpayne@68: return cls jpayne@68: jpayne@68: jpayne@68: class Policy(_PolicyBase, metaclass=abc.ABCMeta): jpayne@68: jpayne@68: r"""Controls for how messages are interpreted and formatted. jpayne@68: jpayne@68: Most of the classes and many of the methods in the email package accept jpayne@68: Policy objects as parameters. A Policy object contains a set of values and jpayne@68: functions that control how input is interpreted and how output is rendered. jpayne@68: For example, the parameter 'raise_on_defect' controls whether or not an RFC jpayne@68: violation results in an error being raised or not, while 'max_line_length' jpayne@68: controls the maximum length of output lines when a Message is serialized. jpayne@68: jpayne@68: Any valid attribute may be overridden when a Policy is created by passing jpayne@68: it as a keyword argument to the constructor. Policy objects are immutable, jpayne@68: but a new Policy object can be created with only certain values changed by jpayne@68: calling the Policy instance with keyword arguments. Policy objects can jpayne@68: also be added, producing a new Policy object in which the non-default jpayne@68: attributes set in the right hand operand overwrite those specified in the jpayne@68: left operand. jpayne@68: jpayne@68: Settable attributes: jpayne@68: jpayne@68: raise_on_defect -- If true, then defects should be raised as errors. jpayne@68: Default: False. jpayne@68: jpayne@68: linesep -- string containing the value to use as separation jpayne@68: between output lines. Default '\n'. jpayne@68: jpayne@68: cte_type -- Type of allowed content transfer encodings jpayne@68: jpayne@68: 7bit -- ASCII only jpayne@68: 8bit -- Content-Transfer-Encoding: 8bit is allowed jpayne@68: jpayne@68: Default: 8bit. Also controls the disposition of jpayne@68: (RFC invalid) binary data in headers; see the jpayne@68: documentation of the binary_fold method. jpayne@68: jpayne@68: max_line_length -- maximum length of lines, excluding 'linesep', jpayne@68: during serialization. None or 0 means no line jpayne@68: wrapping is done. Default is 78. jpayne@68: jpayne@68: mangle_from_ -- a flag that, when True escapes From_ lines in the jpayne@68: body of the message by putting a `>' in front of jpayne@68: them. This is used when the message is being jpayne@68: serialized by a generator. Default: True. jpayne@68: jpayne@68: message_factory -- the class to use to create new message objects. jpayne@68: If the value is None, the default is Message. jpayne@68: jpayne@68: """ jpayne@68: jpayne@68: raise_on_defect = False jpayne@68: linesep = '\n' jpayne@68: cte_type = '8bit' jpayne@68: max_line_length = 78 jpayne@68: mangle_from_ = False jpayne@68: message_factory = None jpayne@68: jpayne@68: def handle_defect(self, obj, defect): jpayne@68: """Based on policy, either raise defect or call register_defect. jpayne@68: jpayne@68: handle_defect(obj, defect) jpayne@68: jpayne@68: defect should be a Defect subclass, but in any case must be an jpayne@68: Exception subclass. obj is the object on which the defect should be jpayne@68: registered if it is not raised. If the raise_on_defect is True, the jpayne@68: defect is raised as an error, otherwise the object and the defect are jpayne@68: passed to register_defect. jpayne@68: jpayne@68: This method is intended to be called by parsers that discover defects. jpayne@68: The email package parsers always call it with Defect instances. jpayne@68: jpayne@68: """ jpayne@68: if self.raise_on_defect: jpayne@68: raise defect jpayne@68: self.register_defect(obj, defect) jpayne@68: jpayne@68: def register_defect(self, obj, defect): jpayne@68: """Record 'defect' on 'obj'. jpayne@68: jpayne@68: Called by handle_defect if raise_on_defect is False. This method is jpayne@68: part of the Policy API so that Policy subclasses can implement custom jpayne@68: defect handling. The default implementation calls the append method of jpayne@68: the defects attribute of obj. The objects used by the email package by jpayne@68: default that get passed to this method will always have a defects jpayne@68: attribute with an append method. jpayne@68: jpayne@68: """ jpayne@68: obj.defects.append(defect) jpayne@68: jpayne@68: def header_max_count(self, name): jpayne@68: """Return the maximum allowed number of headers named 'name'. jpayne@68: jpayne@68: Called when a header is added to a Message object. If the returned jpayne@68: value is not 0 or None, and there are already a number of headers with jpayne@68: the name 'name' equal to the value returned, a ValueError is raised. jpayne@68: jpayne@68: Because the default behavior of Message's __setitem__ is to append the jpayne@68: value to the list of headers, it is easy to create duplicate headers jpayne@68: without realizing it. This method allows certain headers to be limited jpayne@68: in the number of instances of that header that may be added to a jpayne@68: Message programmatically. (The limit is not observed by the parser, jpayne@68: which will faithfully produce as many headers as exist in the message jpayne@68: being parsed.) jpayne@68: jpayne@68: The default implementation returns None for all header names. jpayne@68: """ jpayne@68: return None jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def header_source_parse(self, sourcelines): jpayne@68: """Given a list of linesep terminated strings constituting the lines of jpayne@68: a single header, return the (name, value) tuple that should be stored jpayne@68: in the model. The input lines should retain their terminating linesep jpayne@68: characters. The lines passed in by the email package may contain jpayne@68: surrogateescaped binary data. jpayne@68: """ jpayne@68: raise NotImplementedError jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def header_store_parse(self, name, value): jpayne@68: """Given the header name and the value provided by the application jpayne@68: program, return the (name, value) that should be stored in the model. jpayne@68: """ jpayne@68: raise NotImplementedError jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def header_fetch_parse(self, name, value): jpayne@68: """Given the header name and the value from the model, return the value jpayne@68: to be returned to the application program that is requesting that jpayne@68: header. The value passed in by the email package may contain jpayne@68: surrogateescaped binary data if the lines were parsed by a BytesParser. jpayne@68: The returned value should not contain any surrogateescaped data. jpayne@68: jpayne@68: """ jpayne@68: raise NotImplementedError jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def fold(self, name, value): jpayne@68: """Given the header name and the value from the model, return a string jpayne@68: containing linesep characters that implement the folding of the header jpayne@68: according to the policy controls. The value passed in by the email jpayne@68: package may contain surrogateescaped binary data if the lines were jpayne@68: parsed by a BytesParser. The returned value should not contain any jpayne@68: surrogateescaped data. jpayne@68: jpayne@68: """ jpayne@68: raise NotImplementedError jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def fold_binary(self, name, value): jpayne@68: """Given the header name and the value from the model, return binary jpayne@68: data containing linesep characters that implement the folding of the jpayne@68: header according to the policy controls. The value passed in by the jpayne@68: email package may contain surrogateescaped binary data. jpayne@68: jpayne@68: """ jpayne@68: raise NotImplementedError jpayne@68: jpayne@68: jpayne@68: @_extend_docstrings jpayne@68: class Compat32(Policy): jpayne@68: jpayne@68: """+ jpayne@68: This particular policy is the backward compatibility Policy. It jpayne@68: replicates the behavior of the email package version 5.1. jpayne@68: """ jpayne@68: jpayne@68: mangle_from_ = True jpayne@68: jpayne@68: def _sanitize_header(self, name, value): jpayne@68: # If the header value contains surrogates, return a Header using jpayne@68: # the unknown-8bit charset to encode the bytes as encoded words. jpayne@68: if not isinstance(value, str): jpayne@68: # Assume it is already a header object jpayne@68: return value jpayne@68: if _has_surrogates(value): jpayne@68: return header.Header(value, charset=_charset.UNKNOWN8BIT, jpayne@68: header_name=name) jpayne@68: else: jpayne@68: return value jpayne@68: jpayne@68: def header_source_parse(self, sourcelines): jpayne@68: """+ jpayne@68: The name is parsed as everything up to the ':' and returned unmodified. jpayne@68: The value is determined by stripping leading whitespace off the jpayne@68: remainder of the first line, joining all subsequent lines together, and jpayne@68: stripping any trailing carriage return or linefeed characters. jpayne@68: jpayne@68: """ jpayne@68: name, value = sourcelines[0].split(':', 1) jpayne@68: value = value.lstrip(' \t') + ''.join(sourcelines[1:]) jpayne@68: return (name, value.rstrip('\r\n')) jpayne@68: jpayne@68: def header_store_parse(self, name, value): jpayne@68: """+ jpayne@68: The name and value are returned unmodified. jpayne@68: """ jpayne@68: return (name, value) jpayne@68: jpayne@68: def header_fetch_parse(self, name, value): jpayne@68: """+ jpayne@68: If the value contains binary data, it is converted into a Header object jpayne@68: using the unknown-8bit charset. Otherwise it is returned unmodified. jpayne@68: """ jpayne@68: return self._sanitize_header(name, value) jpayne@68: jpayne@68: def fold(self, name, value): jpayne@68: """+ jpayne@68: Headers are folded using the Header folding algorithm, which preserves jpayne@68: existing line breaks in the value, and wraps each resulting line to the jpayne@68: max_line_length. Non-ASCII binary data are CTE encoded using the jpayne@68: unknown-8bit charset. jpayne@68: jpayne@68: """ jpayne@68: return self._fold(name, value, sanitize=True) jpayne@68: jpayne@68: def fold_binary(self, name, value): jpayne@68: """+ jpayne@68: Headers are folded using the Header folding algorithm, which preserves jpayne@68: existing line breaks in the value, and wraps each resulting line to the jpayne@68: max_line_length. If cte_type is 7bit, non-ascii binary data is CTE jpayne@68: encoded using the unknown-8bit charset. Otherwise the original source jpayne@68: header is used, with its existing line breaks and/or binary data. jpayne@68: jpayne@68: """ jpayne@68: folded = self._fold(name, value, sanitize=self.cte_type=='7bit') jpayne@68: return folded.encode('ascii', 'surrogateescape') jpayne@68: jpayne@68: def _fold(self, name, value, sanitize): jpayne@68: parts = [] jpayne@68: parts.append('%s: ' % name) jpayne@68: if isinstance(value, str): jpayne@68: if _has_surrogates(value): jpayne@68: if sanitize: jpayne@68: h = header.Header(value, jpayne@68: charset=_charset.UNKNOWN8BIT, jpayne@68: header_name=name) jpayne@68: else: jpayne@68: # If we have raw 8bit data in a byte string, we have no idea jpayne@68: # what the encoding is. There is no safe way to split this jpayne@68: # string. If it's ascii-subset, then we could do a normal jpayne@68: # ascii split, but if it's multibyte then we could break the jpayne@68: # string. There's no way to know so the least harm seems to jpayne@68: # be to not split the string and risk it being too long. jpayne@68: parts.append(value) jpayne@68: h = None jpayne@68: else: jpayne@68: h = header.Header(value, header_name=name) jpayne@68: else: jpayne@68: # Assume it is a Header-like object. jpayne@68: h = value jpayne@68: if h is not None: jpayne@68: # The Header class interprets a value of None for maxlinelen as the jpayne@68: # default value of 78, as recommended by RFC 2822. jpayne@68: maxlinelen = 0 jpayne@68: if self.max_line_length is not None: jpayne@68: maxlinelen = self.max_line_length jpayne@68: parts.append(h.encode(linesep=self.linesep, maxlinelen=maxlinelen)) jpayne@68: parts.append(self.linesep) jpayne@68: return ''.join(parts) jpayne@68: jpayne@68: jpayne@68: compat32 = Compat32()