mirror of
https://github.com/aljazceru/validate_email.git
synced 2025-12-20 07:24:21 +01:00
* removed unused "import socket" * changed broad "except" clause to more specific "except ImportError" * general PEP8 reformat * added "debug" option for logging server responses/errors * return None instead of True/False on several errors that may be temporary
This commit is contained in:
@@ -19,14 +19,17 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import smtplib
|
import smtplib
|
||||||
import socket
|
import logging
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import DNS
|
import DNS
|
||||||
ServerError = DNS.ServerError
|
ServerError = DNS.ServerError
|
||||||
except:
|
except ImportError:
|
||||||
DNS = None
|
DNS = None
|
||||||
class ServerError(Exception): pass
|
|
||||||
|
class ServerError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
# All we are really doing is comparing the input string to one
|
# All we are really doing is comparing the input string to one
|
||||||
# gigantic regular expression. But building that regexp, and
|
# gigantic regular expression. But building that regexp, and
|
||||||
# ensuring its correctness, is made much easier by assembling it
|
# ensuring its correctness, is made much easier by assembling it
|
||||||
@@ -50,7 +53,7 @@ CTEXT = r'[' + NO_WS_CTL + \
|
|||||||
r'\x21-\x27\x2a-\x5b\x5d-\x7e]' # see 3.2.3
|
r'\x21-\x27\x2a-\x5b\x5d-\x7e]' # see 3.2.3
|
||||||
CCONTENT = r'(?:' + CTEXT + r'|' + \
|
CCONTENT = r'(?:' + CTEXT + r'|' + \
|
||||||
QUOTED_PAIR + r')' # see 3.2.3 (NB: The RFC includes COMMENT here
|
QUOTED_PAIR + r')' # see 3.2.3 (NB: The RFC includes COMMENT here
|
||||||
# as well, but that would be circular.)
|
# as well, but that would be circular.)
|
||||||
COMMENT = r'\((?:' + FWS + r'?' + CCONTENT + \
|
COMMENT = r'\((?:' + FWS + r'?' + CCONTENT + \
|
||||||
r')*' + FWS + r'?\)' # see 3.2.3
|
r')*' + FWS + r'?\)' # see 3.2.3
|
||||||
CFWS = r'(?:' + FWS + r'?' + COMMENT + ')*(?:' + \
|
CFWS = r'(?:' + FWS + r'?' + COMMENT + ')*(?:' + \
|
||||||
@@ -81,8 +84,8 @@ ADDR_SPEC = LOCAL_PART + r'@' + DOMAIN # see 3.4.1
|
|||||||
# A valid address will match exactly the 3.4.1 addr-spec.
|
# A valid address will match exactly the 3.4.1 addr-spec.
|
||||||
VALID_ADDRESS_REGEXP = '^' + ADDR_SPEC + '$'
|
VALID_ADDRESS_REGEXP = '^' + ADDR_SPEC + '$'
|
||||||
|
|
||||||
def validate_email(email, check_mx=False,verify=False):
|
|
||||||
|
|
||||||
|
def validate_email(email, check_mx=False, verify=False, debug=False):
|
||||||
"""Indicate whether the given string is a valid email address
|
"""Indicate whether the given string is a valid email address
|
||||||
according to the 'addr-spec' portion of RFC 2822 (see section
|
according to the 'addr-spec' portion of RFC 2822 (see section
|
||||||
3.4.1). Parts of the spec that are marked obsolete are *not*
|
3.4.1). Parts of the spec that are marked obsolete are *not*
|
||||||
@@ -90,13 +93,20 @@ def validate_email(email, check_mx=False,verify=False):
|
|||||||
depend on circular definitions in the spec may not pass, but in
|
depend on circular definitions in the spec may not pass, but in
|
||||||
general this should correctly identify any email address likely
|
general this should correctly identify any email address likely
|
||||||
to be in use as of 2011."""
|
to be in use as of 2011."""
|
||||||
|
if debug:
|
||||||
|
logger = logging.getLogger('validate_email')
|
||||||
|
else:
|
||||||
|
logger = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
assert re.match(VALID_ADDRESS_REGEXP, email) is not None
|
assert re.match(VALID_ADDRESS_REGEXP, email) is not None
|
||||||
check_mx |= verify
|
check_mx |= verify
|
||||||
if check_mx:
|
if check_mx:
|
||||||
if not DNS: raise Exception('For check the mx records or check if the email exists you must have installed pyDNS python package')
|
if not DNS:
|
||||||
|
raise Exception('For check the mx records or check if the email exists you must '
|
||||||
|
'have installed pyDNS python package')
|
||||||
DNS.DiscoverNameServers()
|
DNS.DiscoverNameServers()
|
||||||
hostname = email[email.find('@')+1:]
|
hostname = email[email.find('@') + 1:]
|
||||||
mx_hosts = DNS.mxlookup(hostname)
|
mx_hosts = DNS.mxlookup(hostname)
|
||||||
for mx in mx_hosts:
|
for mx in mx_hosts:
|
||||||
try:
|
try:
|
||||||
@@ -108,20 +118,30 @@ def validate_email(email, check_mx=False,verify=False):
|
|||||||
status, _ = smtp.helo()
|
status, _ = smtp.helo()
|
||||||
if status != 250:
|
if status != 250:
|
||||||
smtp.quit()
|
smtp.quit()
|
||||||
|
if debug:
|
||||||
|
logger.debug(u'%s answer: %s - %s', mx[1], status, _)
|
||||||
continue
|
continue
|
||||||
smtp.mail('')
|
smtp.mail('')
|
||||||
status, _ = smtp.rcpt(email)
|
status, _ = smtp.rcpt(email)
|
||||||
if status != 250:
|
if status == 250:
|
||||||
smtp.quit()
|
smtp.quit()
|
||||||
return False
|
return True
|
||||||
|
if debug:
|
||||||
|
logger.debug(u'%s answer: %s - %s', mx[1], status, _)
|
||||||
smtp.quit()
|
smtp.quit()
|
||||||
break
|
except smtplib.SMTPServerDisconnected: # Server not permits verify user
|
||||||
except smtplib.SMTPServerDisconnected: #Server not permits verify user
|
if debug:
|
||||||
break
|
logger.debug(u'%s disconected.', mx[1])
|
||||||
except smtplib.SMTPConnectError:
|
except smtplib.SMTPConnectError:
|
||||||
continue
|
if debug:
|
||||||
except (AssertionError, ServerError):
|
logger.debug(u'Unable to connect to %s.', mx[1])
|
||||||
|
return None
|
||||||
|
except AssertionError:
|
||||||
return False
|
return False
|
||||||
|
except ServerError:
|
||||||
|
if debug:
|
||||||
|
logger.debug(u'ServerError exception raised.')
|
||||||
|
return None
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# import sys
|
# import sys
|
||||||
|
|||||||
Reference in New Issue
Block a user