66 lines
2.9 KiB
Python
66 lines
2.9 KiB
Python
# All we are really doing is comparing the input string to one
|
|
# gigantic regular expression. But building that regexp, and
|
|
# ensuring its correctness, is made much easier by assembling it
|
|
# from the "tokens" defined by the RFC. Each of these tokens is
|
|
# tested in the accompanying unit test file.
|
|
#
|
|
# The section of RFC 2822 from which each pattern component is
|
|
# derived is given in an accompanying comment.
|
|
#
|
|
# (To make things simple, every string below is given as 'raw',
|
|
# even when it's not strictly necessary. This way we don't forget
|
|
# when it is necessary.)
|
|
#
|
|
|
|
|
|
import re
|
|
|
|
|
|
WSP = r'[\s]' # see 2.2.2. Structured Header Field Bodies
|
|
CRLF = r'(?:\r\n)' # see 2.2.3. Long Header Fields
|
|
NO_WS_CTL = r'\x01-\x08\x0b\x0c\x0f-\x1f\x7f' # see 3.2.1. Primitive Tokens
|
|
QUOTED_PAIR = r'(?:\\.)' # see 3.2.2. Quoted characters
|
|
FWS = r'(?:(?:' + WSP + r'*' + CRLF + r')?' + \
|
|
WSP + r'+)' # see 3.2.3. Folding white space and comments
|
|
CTEXT = r'[' + NO_WS_CTL + \
|
|
r'\x21-\x27\x2a-\x5b\x5d-\x7e]' # see 3.2.3
|
|
CCONTENT = r'(?:' + CTEXT + r'|' + \
|
|
QUOTED_PAIR + r')' # see 3.2.3 (NB: The RFC includes COMMENT here
|
|
# as well, but that would be circular.)
|
|
COMMENT = r'\((?:' + FWS + r'?' + CCONTENT + \
|
|
r')*' + FWS + r'?\)' # see 3.2.3
|
|
CFWS = r'(?:' + FWS + r'?' + COMMENT + ')*(?:' + \
|
|
FWS + '?' + COMMENT + '|' + FWS + ')' # see 3.2.3
|
|
ATEXT = r'[\w!#$%&\'\*\+\-/=\?\^`\{\|\}~]' # see 3.2.4. Atom
|
|
ATOM = CFWS + r'?' + ATEXT + r'+' + CFWS + r'?' # see 3.2.4
|
|
DOT_ATOM_TEXT = ATEXT + r'+(?:\.' + ATEXT + r'+)*' # see 3.2.4
|
|
DOT_ATOM = CFWS + r'?' + DOT_ATOM_TEXT + CFWS + r'?' # see 3.2.4
|
|
QTEXT = r'[' + NO_WS_CTL + \
|
|
r'\x21\x23-\x5b\x5d-\x7e]' # see 3.2.5. Quoted strings
|
|
QCONTENT = r'(?:' + QTEXT + r'|' + \
|
|
QUOTED_PAIR + r')' # see 3.2.5
|
|
QUOTED_STRING = CFWS + r'?' + r'"(?:' + FWS + \
|
|
r'?' + QCONTENT + r')*' + FWS + \
|
|
r'?' + r'"' + CFWS + r'?'
|
|
LOCAL_PART = r'(?:' + DOT_ATOM + r'|' + \
|
|
QUOTED_STRING + r')' # see 3.4.1. Addr-spec specification
|
|
DTEXT = r'[' + NO_WS_CTL + r'\x21-\x5a\x5e-\x7e]' # see 3.4.1
|
|
DCONTENT = r'(?:' + DTEXT + r'|' + \
|
|
QUOTED_PAIR + r')' # see 3.4.1
|
|
DOMAIN_LITERAL = CFWS + r'?' + r'\[' + \
|
|
r'(?:' + FWS + r'?' + DCONTENT + \
|
|
r')*' + FWS + r'?\]' + CFWS + r'?' # see 3.4.1
|
|
DOMAIN = r'(?:' + DOT_ATOM + r'|' + \
|
|
DOMAIN_LITERAL + r')' # see 3.4.1
|
|
ADDR_SPEC = LOCAL_PART + r'@' + DOMAIN # see 3.4.1
|
|
VALID_ADDRESS_REGEXP = '^' + ADDR_SPEC + '$'
|
|
|
|
|
|
def email_has_valid_structure(email_address):
|
|
if re.match(VALID_ADDRESS_REGEXP, email_address):
|
|
return True
|
|
return False
|
|
|
|
|
|
|