83 lines
3.1 KiB
Python
83 lines
3.1 KiB
Python
from logging import getLogger
|
|
from ssl import SSLContext
|
|
from typing import Optional
|
|
|
|
from .dns_check import dns_check, DefaultAddressTypes, AddressTypes
|
|
from .domainlist_check import domainlist_check
|
|
from .email_address import EmailAddress
|
|
from .exceptions import (
|
|
AddressFormatError, EmailValidationError, FromAddressFormatError,
|
|
SMTPTemporaryError)
|
|
from .regex_check import regex_check
|
|
from .smtp_check import smtp_check
|
|
|
|
LOGGER = getLogger(name=__name__)
|
|
|
|
__all__ = ['validate_email', 'validate_email_or_fail']
|
|
__doc__ = """\
|
|
Verify the given email address by determining the SMTP servers
|
|
responsible for the domain and then asking them to deliver an email to
|
|
the address. Before the actual message is sent, the process is
|
|
interrupted.
|
|
|
|
PLEASE NOTE: Some email providers only tell the actual delivery failure
|
|
AFTER having delivered the body which this module doesn't, while others
|
|
simply accept everything and send a bounce notification later. Hence, a
|
|
100% proper response is not guaranteed.
|
|
"""
|
|
|
|
|
|
def validate_email_or_fail(
|
|
email_address: str, *, check_format: bool = True,
|
|
check_blacklist: bool = True, check_dns: bool = True,
|
|
dns_timeout: float = 10, check_smtp: bool = True,
|
|
smtp_timeout: float = 10, smtp_helo_host: Optional[str] = None,
|
|
smtp_from_address: Optional[str] = None,
|
|
smtp_skip_tls: bool = False, smtp_tls_context: Optional[SSLContext] = None,
|
|
smtp_debug: bool = False, address_types: AddressTypes = DefaultAddressTypes
|
|
) -> Optional[bool]:
|
|
"""
|
|
Return `True` if the email address validation is successful, `None`
|
|
if the validation result is ambiguous, and raise an exception if the
|
|
validation fails.
|
|
"""
|
|
email_address_to = EmailAddress(address=email_address)
|
|
if check_format:
|
|
regex_check(email_address=email_address_to)
|
|
if check_blacklist:
|
|
domainlist_check(email_address=email_address_to)
|
|
if not check_dns and not check_smtp: # check_smtp implies check_dns.
|
|
return True
|
|
mx_records = dns_check(
|
|
email_address=email_address_to, timeout=dns_timeout,
|
|
address_types=address_types)
|
|
if not check_smtp:
|
|
return True
|
|
try:
|
|
email_address_from = None if not smtp_from_address else \
|
|
EmailAddress(address=smtp_from_address)
|
|
except AddressFormatError:
|
|
raise FromAddressFormatError
|
|
return smtp_check(
|
|
email_address=email_address_to, mx_records=mx_records,
|
|
timeout=smtp_timeout, helo_host=smtp_helo_host,
|
|
from_address=email_address_from, skip_tls=smtp_skip_tls,
|
|
tls_context=smtp_tls_context, debug=smtp_debug)
|
|
|
|
|
|
def validate_email(email_address: str, **kwargs):
|
|
"""
|
|
Return `True` or `False` depending if the email address exists
|
|
or/and can be delivered.
|
|
|
|
Return `None` if the result is ambiguous.
|
|
"""
|
|
try:
|
|
return validate_email_or_fail(email_address, **kwargs)
|
|
except SMTPTemporaryError as error:
|
|
LOGGER.info(
|
|
msg=f'Validation for {email_address!r} is ambiguous: {error}')
|
|
return
|
|
except EmailValidationError as error:
|
|
LOGGER.info(msg=f'Validation for {email_address!r} failed: {error}')
|
|
return False
|