def full_spec(self): """ Returns an ASCII-compatable encoding of an email address or raises a ValueError. Display name and domain parts will be converted to ASCII-compatable encoding. The transformed address will be ASCII-only and RFC-2822 compliant. >>> EmailAddress("Ev K", "*****@*****.**").full_spec() 'Ev K <*****@*****.**>' >>> EmailAddress("Жека", "*****@*****.**").full_spec() '=?utf-8?b?0JbQtdC60LA=?= <*****@*****.**>' """ if not is_pure_ascii(self.mailbox): raise ValueError( 'address {} has no ASCII-compatable encoding'.format( self.address.encode('utf-8'))) if not is_pure_ascii(self.hostname): try: ace_hostname = idna.encode(self.hostname) except idna.IDNAError: raise ValueError( 'address {} has no ASCII-compatable encoding'.format( self.address.encode('utf-8'))) else: ace_hostname = self.hostname if self.display_name: ace_display_name = smart_quote( encode_string(None, self.display_name, maxlinelen=MAX_ADDRESS_LENGTH)) return u'{} <{}@{}>'.format(ace_display_name, self.mailbox, ace_hostname) return u'{}@{}'.format(self.mailbox, ace_hostname)
def ace_address(self): if not is_pure_ascii(self.mailbox): raise ValueError('address {} has no ASCII-compatable encoding' .format(self.address.encode('utf-8'))) ace_hostname = self.hostname if not is_pure_ascii(self.hostname): try: ace_hostname = idna.encode(self.hostname) except idna.IDNAError: raise ValueError('address {} has no ASCII-compatable encoding' .format(self.address.encode('utf-8'))) return '{}@{}'.format(self.mailbox, ace_hostname)
def requires_non_ascii(self): """ Can the address be converted to an ASCII compatible encoding? """ if not is_pure_ascii(self.mailbox): return True if not is_pure_ascii(self.hostname): try: idna.encode(self.hostname) except idna.IDNAError: return True return False
def parse_header_value(name, val): if not is_pure_ascii(val): val = to_unicode(val) if parametrized.is_parametrized(name, val): val, params = parametrized.decode(val) if val is not None and not is_pure_ascii(val): raise DecodingError('Non-ascii content header value') if name == 'Content-Type': main, sub = parametrized.fix_content_type(val) return ContentType(main, sub, params) return WithParams(val, params) return val
def ace_address(self): if not is_pure_ascii(self.mailbox): raise ValueError( 'address {} has no ASCII-compatable encoding'.format( self.address.encode('utf-8'))) ace_hostname = self.hostname if not is_pure_ascii(self.hostname): try: ace_hostname = idna.encode(self.hostname) except idna.IDNAError: raise ValueError( 'address {} has no ASCII-compatable encoding'.format( self.address.encode('utf-8'))) return '{}@{}'.format(self.mailbox, ace_hostname)
def parse_header_value(name, val): if not is_pure_ascii(val): if parametrized.is_parametrized(name, val): # PHOENIX HACK!!! # FIXME: making the function return a completely different type on # parameterized headers is wrong. val = val.decode('utf-8', errors='replace') val = val.split(";", 1) if len(val) == 2: val, params = val rparams = {} for p in params.split(";"): k,v = p.split("=", 1) rparams[k.strip()] = v.split('"')[1] return val, rparams return val[0], {} return to_unicode(val) else: if parametrized.is_parametrized(name, val): val, params = parametrized.decode(val) if name == 'Content-Type': main, sub = parametrized.fix_content_type(val) return ContentType(main, sub, params) else: return WithParams(val, params) else: return val
def address_spec(self, stream): ''' Extract a single address spec from a stream of input, always operates in strict mode. ''' # sanity check if stream is None: raise ParserException('No input provided to parser.') if isinstance(stream, str) and not is_pure_ascii(stream): raise ParserException('ASCII string contains non-ASCII chars.') # to avoid spinning here forever, limit mailbox length if len(stream) > MAX_ADDRESS_LENGTH: raise ParserException('Stream length exceeds maximum allowable ' + \ 'address length of ' + str(MAX_ADDRESS_LENGTH) + '.') self.stream = TokenStream(stream) addr = self._addr_spec() if addr: # optional whitespace self._whitespace() # if we hit the end of the stream, we have a valid inbox if self.stream.end_of_stream(): return addr return None
def __init__( self, content_type, body, charset=None, disposition=None, filename=None): self.headers = headers.MimeHeaders() self.body = body self.disposition = disposition or ('attachment' if filename else None) self.filename = filename self.size = len(body) if self.filename: self.filename = path.basename(self.filename) content_type = adjust_content_type(content_type, body, filename) if content_type.main == 'text': # the text should have a charset if not charset: charset = "utf-8" # it should be stored as unicode. period self.body = charsets.convert_to_unicode(charset, body) # let's be simple when possible if charset != 'ascii' and is_pure_ascii(body): charset = 'ascii' self.headers['MIME-Version'] = '1.0' self.headers['Content-Type'] = content_type if charset: content_type.params['charset'] = charset if self.disposition: self.headers['Content-Disposition'] = WithParams(disposition) if self.filename: self.headers['Content-Disposition'].params['filename'] = self.filename self.headers['Content-Type'].params['name'] = self.filename
def parse_header(header): """ Accepts a raw header with name, colons and newlines and returns it's parsed value """ name, val = split2(header) if not is_pure_ascii(name): raise DecodingError("Non-ascii header name") return name, parse_header_value(name, encodedword.unfold(val))
def _whitespace_unicode(self): """ Grammar: whitespace-unicode -> whitespace-unicode """ uwhite = self.stream.get_token(UNI_WHITE) if uwhite and not is_pure_ascii(uwhite): return uwhite return None
def validate_address(addr_spec, metrics=False): """ Given an addr-spec, runs the pre-parser, the parser, DNS MX checks, MX existence checks, and if available, ESP specific grammar for the local part. In the case of a valid address returns an EmailAddress object, otherwise returns None. If requested, will also return the parsing time metrics. Examples: >>> address.validate_address('*****@*****.**') None >>> address.validate_address('*****@*****.**') None >>> address.validate_address('*****@*****.**') [email protected] """ mtimes = {'parsing': 0, 'mx_lookup': 0, 'dns_lookup': 0, 'mx_conn':0 , 'custom_grammar':0} # sanity check if addr_spec is None: return None, mtimes if not is_pure_ascii(addr_spec): return None, mtimes # preparse address into its parts and perform any ESP specific pre-parsing addr_parts = flanker.addresslib.validate.preparse_address(addr_spec) if addr_parts is None: return None, mtimes # run parser against address bstart = time.time() paddr = parse('@'.join(addr_parts), addr_spec_only=True) mtimes['parsing'] = time.time() - bstart if paddr is None: return None, mtimes # lookup if this domain has a mail exchanger exchanger, mx_metrics = \ flanker.addresslib.validate.mail_exchanger_lookup(addr_parts[-1], metrics=True) mtimes['mx_lookup'] = mx_metrics['mx_lookup'] mtimes['dns_lookup'] = mx_metrics['dns_lookup'] mtimes['mx_conn'] = mx_metrics['mx_conn'] if exchanger is None: return None, mtimes # lookup custom local-part grammar if it exists bstart = time.time() plugin = flanker.addresslib.validate.plugin_for_esp(exchanger) mtimes['custom_grammar'] = time.time() - bstart if plugin and plugin.validate(addr_parts[0]) is False: return None, mtimes return paddr, mtimes
def validate_address(addr_spec, metrics=False): ''' Given an addr-spec, runs the pre-parser, the parser, DNS MX checks, MX existence checks, and if available, ESP specific grammar for the local part. In the case of a valid address returns an EmailAddress object, otherwise returns None. If requested, will also return the parsing time metrics. Examples: >>> address.validate_address('*****@*****.**') None >>> address.validate_address('*****@*****.**') None >>> address.validate_address('*****@*****.**') [email protected] ''' mtimes = {'parsing': 0, 'mx_lookup': 0, 'dns_lookup': 0, 'mx_conn':0 , 'custom_grammar':0} # sanity check if addr_spec is None: return None, mtimes if not is_pure_ascii(addr_spec): return None, mtimes # preparse address into its parts and perform any ESP specific pre-parsing addr_parts = flanker.addresslib.validate.preparse_address(addr_spec) if addr_parts is None: return None, mtimes # run parser against address bstart = time.time() paddr = parse('@'.join(addr_parts), addr_spec_only=True) mtimes['parsing'] = time.time() - bstart if paddr is None: return None, mtimes # lookup if this domain has a mail exchanger exchanger, mx_metrics = \ flanker.addresslib.validate.mail_exchanger_lookup(addr_parts[-1], metrics=True) mtimes['mx_lookup'] = mx_metrics['mx_lookup'] mtimes['dns_lookup'] = mx_metrics['dns_lookup'] mtimes['mx_conn'] = mx_metrics['mx_conn'] if exchanger is None: return None, mtimes # lookup custom local-part grammar if it exists bstart = time.time() plugin = flanker.addresslib.validate.plugin_for_esp(exchanger) mtimes['custom_grammar'] = time.time() - bstart if plugin and plugin.validate(addr_parts[0]) is False: return None, mtimes return paddr, mtimes
def parse_header_value(name, val): if parametrized.is_parametrized(name, val): val, params = parametrized.decode(val) if name == 'Content-Type': main, sub = parametrized.fix_content_type(val) return ContentType(main, sub, params) else: return WithParams(val, params) else: return val if is_pure_ascii(val) else to_unicode(val)
def __init__(self, raw_display_name=None, raw_addr_spec=None, _display_name=None, _mailbox=None, _hostname=None): raw_display_name = _to_parser_input(raw_display_name) raw_addr_spec = _to_parser_input(raw_addr_spec) if raw_display_name and raw_addr_spec: mailbox = addr_spec_parser.parse(raw_addr_spec, lexer.clone()) self._display_name = _to_text(raw_display_name) self._mailbox = _to_text(mailbox.local_part) self._hostname = _to_text(mailbox.domain) elif raw_display_name: mailbox = mailbox_parser.parse(raw_display_name, lexer.clone()) self._display_name = _to_text(mailbox.display_name) self._mailbox = _to_text(mailbox.local_part) self._hostname = _to_text(mailbox.domain) elif raw_addr_spec: mailbox = addr_spec_parser.parse(raw_addr_spec, lexer.clone()) self._display_name = u'' self._mailbox = _to_text(mailbox.local_part) self._hostname = _to_text(mailbox.domain) elif _mailbox and _hostname: self._display_name = _display_name or u'' self._mailbox = _mailbox self._hostname = _hostname else: raise SyntaxError('failed to create EmailAddress: bad parameters') # Convert display name to decoded unicode string. if (self._display_name.startswith('=?') and self._display_name.endswith('?=')): self._display_name = mime_to_unicode(self._display_name) if (self._display_name.startswith('"') and self._display_name.endswith('"') and len(self._display_name) > 2): self._display_name = smart_unquote(self._display_name) # Convert hostname to lowercase unicode string. self._hostname = self._hostname.lower() if self._hostname.startswith('xn--') or '.xn--' in self._hostname: self._hostname = idna.decode(self._hostname) if not is_pure_ascii(self._hostname): idna.encode(self._hostname) assert isinstance(self._display_name, six.text_type) assert isinstance(self._mailbox, six.text_type) assert isinstance(self._hostname, six.text_type)
def parse_header_value(name, val): if not is_pure_ascii(val): if parametrized.is_parametrized(name, val): raise DecodingError("Unsupported value in content- header") return to_unicode(val) else: if parametrized.is_parametrized(name, val): val, params = parametrized.decode(val) if name == 'Content-Type': main, sub = parametrized.fix_content_type(val) return ContentType(main, sub, params) else: return WithParams(val, params) else: return val
def address_list(self, stream): ''' Extract a mailbox and/or url list from a stream of input, operates in strict and relaxed modes. ''' # sanity check if not stream: raise ParserException('No input provided to parser.') if isinstance(stream, str) and not is_pure_ascii(stream): raise ParserException('ASCII string contains non-ASCII chars.') # to avoid spinning here forever, limit address list length if len(stream) > MAX_ADDRESS_LIST_LENGTH: raise ParserException('Stream length exceeds maximum allowable ' + \ 'address list length of ' + str(MAX_ADDRESS_LIST_LENGTH) + '.') # set stream self.stream = TokenStream(stream) if self.strict is True: return self._address_list_strict() return self._address_list_relaxed()
def contains_non_ascii(self): """ Does the address contain any non-ASCII characters? """ return not is_pure_ascii(self.address)
def __init__(self, raw_display_name=None, raw_addr_spec=None, display_name=None, mailbox=None, hostname=None): if isinstance(raw_display_name, unicode): raw_display_name = raw_display_name.encode('utf-8') if isinstance(raw_addr_spec, unicode): raw_addr_spec = raw_addr_spec.encode('utf-8') if raw_display_name and raw_addr_spec: parser = addr_spec_parser mailbox = parser.parse(raw_addr_spec.strip(), lexer=lexer.clone()) self._display_name = raw_display_name self._mailbox = mailbox.local_part self._hostname = mailbox.domain elif raw_display_name: parser = mailbox_parser mailbox = parser.parse(raw_display_name.strip(), lexer=lexer.clone()) self._display_name = mailbox.display_name self._mailbox = mailbox.local_part self._hostname = mailbox.domain elif raw_addr_spec: parser = addr_spec_parser mailbox = parser.parse(raw_addr_spec.strip(), lexer=lexer.clone()) self._display_name = '' self._mailbox = mailbox.local_part self._hostname = mailbox.domain elif mailbox and hostname: self._display_name = display_name or '' self._mailbox = mailbox self._hostname = hostname else: raise SyntaxError('failed to create EmailAddress: bad parameters') # Convert display name to decoded unicode string. if (self._display_name.startswith('=?') and self._display_name.endswith('?=')): self._display_name = mime_to_unicode(self._display_name) if (self._display_name.startswith('"') and self._display_name.endswith('"') and len(self._display_name) > 2): self._display_name = smart_unquote(self._display_name) if isinstance(self._display_name, str): self._display_name = self._display_name.decode('utf-8') # Convert localpart to unicode string. if isinstance(self._mailbox, str): self._mailbox = self._mailbox.decode('utf-8') # Convert hostname to lowercase unicode string. self._hostname = self._hostname.lower() if self._hostname.startswith('xn--') or '.xn--' in self._hostname: self._hostname = idna.decode(self._hostname) if isinstance(self._hostname, str): self._hostname = self._hostname.decode('utf-8') if not is_pure_ascii(self._hostname): idna.encode(self._hostname)
def ace_address(self): if not is_pure_ascii(self._mailbox): raise ValueError('Address {} has no ASCII-compatable encoding' .format(self.address)) return _to_str('{}@{}'.format(self._mailbox, self.ace_hostname))
def ace_address(self): if not is_pure_ascii(self._mailbox): raise ValueError( 'Address {} has no ASCII-compatable encoding'.format( self.address)) return _to_str('{}@{}'.format(self._mailbox, self.ace_hostname))