Exemple #1
0
    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)
Exemple #2
0
 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)
Exemple #3
0
 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
Exemple #4
0
 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
Exemple #5
0
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
Exemple #6
0
 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)
Exemple #7
0
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
Exemple #8
0
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
Exemple #9
0
    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
Exemple #10
0
    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
Exemple #11
0
    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
Exemple #12
0
    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
Exemple #13
0
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))
Exemple #14
0
 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
Exemple #15
0
 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
Exemple #16
0
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))
Exemple #17
0
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
Exemple #18
0
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
Exemple #19
0
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)
Exemple #20
0
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)
Exemple #21
0
    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)
Exemple #22
0
    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)
Exemple #23
0
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
Exemple #24
0
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
Exemple #25
0
    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()
Exemple #26
0
    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()
Exemple #27
0
 def contains_non_ascii(self):
     """
     Does the address contain any non-ASCII characters?
     """
     return not is_pure_ascii(self.address)
Exemple #28
0
 def contains_non_ascii(self):
     """
     Does the address contain any non-ASCII characters?
     """
     return not is_pure_ascii(self.address)
Exemple #29
0
    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)
Exemple #30
0
 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))
Exemple #31
0
 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))
Exemple #32
0
    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)