Reorganised code; mx check in separate file
This commit is contained in:
parent
9f9fc94875
commit
a110914328
8 changed files with 92 additions and 199 deletions
3
AUTHORS
3
AUTHORS
|
@ -1,4 +1,5 @@
|
||||||
validate_email was extended and updated for use with Python 3 by Ben Baert <ben_b@gmx.com> in May 2018.
|
validate_email was extended and updated for use with Python 3
|
||||||
|
by Ben Baert <ben_b@gmx.com> in May 2018.
|
||||||
validate_email was created by Syrus Akbary <me@syrusakbary.com> in
|
validate_email was created by Syrus Akbary <me@syrusakbary.com> in
|
||||||
April 2012.
|
April 2012.
|
||||||
This package is based on the work of Noel Bush <noel@platformer.org>
|
This package is based on the work of Noel Bush <noel@platformer.org>
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
Thanks for downloading validate_email.
|
|
||||||
|
|
||||||
To install it, make sure you have Python 3 and dnspython installed. Then run
|
|
||||||
this command from the command prompt:
|
|
||||||
|
|
||||||
python setup.py install
|
|
19
README.rst
19
README.rst
|
@ -1,24 +1,17 @@
|
||||||
==============
|
==============
|
||||||
Validate_email
|
pyemailval
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Validate_email is a package for Python that check if an email is valid, properly formatted and really exists.
|
pyemailval is a package for Python that check if an email is valid, properly formatted and really exists.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
INSTALLATION
|
INSTALLATION
|
||||||
============
|
============
|
||||||
|
|
||||||
First, you must do::
|
You can install the package with pip:
|
||||||
|
|
||||||
pip install validate_email
|
pip install pyemailval
|
||||||
|
|
||||||
Extra
|
|
||||||
------
|
|
||||||
|
|
||||||
For check the domain mx and verify email exits you must have the `pyDNS` package installed::
|
|
||||||
|
|
||||||
pip install pyDNS
|
|
||||||
|
|
||||||
|
|
||||||
USAGE
|
USAGE
|
||||||
|
@ -26,7 +19,7 @@ USAGE
|
||||||
|
|
||||||
Basic usage::
|
Basic usage::
|
||||||
|
|
||||||
from validate_email import validate_email
|
from pyemailval import validate_email
|
||||||
is_valid = validate_email('example@example.com')
|
is_valid = validate_email('example@example.com')
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,4 +43,4 @@ Check if the host has SMTP Server and the email really exists::
|
||||||
|
|
||||||
TODOs and BUGS
|
TODOs and BUGS
|
||||||
==============
|
==============
|
||||||
See: http://github.com/syrusakbary/validate_email/issues
|
See: http://github.com/Ben-Baert/pyemailval/issues
|
|
@ -56,10 +56,18 @@ ADDR_SPEC = LOCAL_PART + r'@' + DOMAIN # see 3.4.1
|
||||||
VALID_ADDRESS_REGEXP = '^' + ADDR_SPEC + '$'
|
VALID_ADDRESS_REGEXP = '^' + ADDR_SPEC + '$'
|
||||||
|
|
||||||
|
|
||||||
|
def get_domain_from_email_address(email_address):
|
||||||
|
try:
|
||||||
|
return re.search(r"(?<=@)\[?([^\[\]]+)", email_address)[1]
|
||||||
|
except TypeError:
|
||||||
|
raise ValueError("Invalid email address")
|
||||||
|
except IndexError:
|
||||||
|
raise ValueError("Invalid email address")
|
||||||
|
|
||||||
|
|
||||||
def email_has_valid_structure(email_address):
|
def email_has_valid_structure(email_address):
|
||||||
|
if any(ord(char) > 127 for char in email_address):
|
||||||
|
return False
|
||||||
if re.match(VALID_ADDRESS_REGEXP, email_address):
|
if re.match(VALID_ADDRESS_REGEXP, email_address):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
37
mx_check.py
Normal file
37
mx_check.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import socket
|
||||||
|
import smtplib
|
||||||
|
import dns.resolver as dns
|
||||||
|
|
||||||
|
from email_regex import get_domain_from_email_address
|
||||||
|
|
||||||
|
|
||||||
|
def get_mx_records(domain):
|
||||||
|
try:
|
||||||
|
records = dns.query(domain, 'MX')
|
||||||
|
except dns.NXDOMAIN:
|
||||||
|
raise ValueError("Domain {} does not seem to exist")
|
||||||
|
except:
|
||||||
|
raise NotImplementedError("Feature not yet implemented")
|
||||||
|
return [str(x.exchange) for x in records]
|
||||||
|
|
||||||
|
|
||||||
|
def mx_check(email_address):
|
||||||
|
host = socket.gethostname()
|
||||||
|
|
||||||
|
smtp = smtplib.SMTP()
|
||||||
|
smtp.set_debuglevel(0)
|
||||||
|
|
||||||
|
domain = get_domain_from_email_address(email_address)
|
||||||
|
mx_records = get_mx_records(domain)
|
||||||
|
print(mx_records)
|
||||||
|
|
||||||
|
for mx_record in mx_records:
|
||||||
|
smtp.connect(mx_record)
|
||||||
|
smtp.helo(host)
|
||||||
|
smtp.mail(email_address)
|
||||||
|
code, message = smtp.rcpt(email_address)
|
||||||
|
smtp.quit()
|
||||||
|
return True
|
||||||
|
if code == 250:
|
||||||
|
return True
|
||||||
|
return False
|
|
@ -1,5 +1,15 @@
|
||||||
|
from email_regex import get_domain_from_email_address
|
||||||
from email_regex import email_has_valid_structure
|
from email_regex import email_has_valid_structure
|
||||||
|
|
||||||
|
DOMAINS = {
|
||||||
|
"email@domain.com": "domain.com",
|
||||||
|
"email@subdomain.domain.com": "subdomain.domain.com",
|
||||||
|
"email@123.123.123.123": "123.123.123.123",
|
||||||
|
"email@[123.123.123.123]": "123.123.123.123",
|
||||||
|
"email@domain-one.com": "domain-one.com",
|
||||||
|
"email@domain.co.jp": "domain.co.jp",
|
||||||
|
}
|
||||||
|
|
||||||
VALID_EMAIL_ADDRESS_EXAMPLES = [
|
VALID_EMAIL_ADDRESS_EXAMPLES = [
|
||||||
"email@domain.com", # basic valid email
|
"email@domain.com", # basic valid email
|
||||||
"firstname.lastname@domain.com", # dot in address field
|
"firstname.lastname@domain.com", # dot in address field
|
||||||
|
@ -36,14 +46,25 @@ INVALID_EMAIL_ADDRESS_EXAMPLES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_domain_from_email_address():
|
||||||
|
for email_address, domain in DOMAINS.items():
|
||||||
|
try:
|
||||||
|
domain_from_function = get_domain_from_email_address(email_address)
|
||||||
|
assert domain_from_function == domain
|
||||||
|
except AssertionError:
|
||||||
|
raise AssertionError(
|
||||||
|
"Email address {} should result in domain {} but resulted in domain {}"
|
||||||
|
.format(email_address, domain, domain_from_function))
|
||||||
|
|
||||||
|
|
||||||
def test_valid_email_structure_regex():
|
def test_valid_email_structure_regex():
|
||||||
for index, valid_email_address in enumerate(VALID_EMAIL_ADDRESS_EXAMPLES):
|
for index, valid_email_address in enumerate(VALID_EMAIL_ADDRESS_EXAMPLES):
|
||||||
try:
|
try:
|
||||||
assert email_has_valid_structure(valid_email_address) is True
|
assert email_has_valid_structure(valid_email_address) is True
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
("{} should be valid ({}th email address in the list)"
|
"{} should be valid ({}th email address in the list)"
|
||||||
.format(valid_email_address, index)))
|
.format(valid_email_address, index))
|
||||||
|
|
||||||
def test_invalid_email_structure_regex():
|
def test_invalid_email_structure_regex():
|
||||||
for index, invalid_email_address in enumerate(INVALID_EMAIL_ADDRESS_EXAMPLES):
|
for index, invalid_email_address in enumerate(INVALID_EMAIL_ADDRESS_EXAMPLES):
|
||||||
|
@ -51,5 +72,5 @@ def test_invalid_email_structure_regex():
|
||||||
assert email_has_valid_structure(invalid_email_address) is False
|
assert email_has_valid_structure(invalid_email_address) is False
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
("{} should be invalid ({}th email address in the list)"
|
"{} should be invalid ({}th email address in the list)"
|
||||||
.format(invalid_email_address, index)))
|
.format(invalid_email_address, index))
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
How to Set Up Validate Email Using Vagrant on PC
|
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
1. Download Vagrant & Virtual Box
|
|
||||||
* http://www.vagrantup.com/downloads
|
|
||||||
* http://www.virtualbox.org/
|
|
||||||
2. Look at http://docs.vagrantup.com/v2/getting-started/ to make a linux virtual machine via vagrant
|
|
||||||
* tl;dr using bash (Can use regular Windows Terminal)
|
|
||||||
* Make a folder first, whenever you want to access the vm you will need to cd into that folder
|
|
||||||
```
|
|
||||||
$ vagrant init hashicorp/precise32
|
|
||||||
|
|
||||||
$ vagrant up
|
|
||||||
|
|
||||||
$ vagrant ssh
|
|
||||||
```
|
|
||||||
* This last command is used to go into your vm
|
|
||||||
3. Install pipin the vm
|
|
||||||
* http://www.saltycrane.com/blog/2010/02/how-install-pip-ubuntu/
|
|
||||||
* tl;dr
|
|
||||||
```
|
|
||||||
$ sudo apt-get install python-pip
|
|
||||||
|
|
||||||
```
|
|
||||||
4. Install pyDNS
|
|
||||||
```
|
|
||||||
This is a package dependency of validate email
|
|
||||||
|
|
||||||
$ sudo pip install pydns
|
|
||||||
```
|
|
||||||
5. Install git
|
|
||||||
```
|
|
||||||
$ sudo apt-get install git
|
|
||||||
```
|
|
||||||
6. Clone the validate_email repo to your vm
|
|
||||||
* (Since its a new machine you will need to clone using the https url)
|
|
||||||
* ```$ git clone git@github.com:efagerberg/validate_email.git```
|
|
||||||
* If you want to use your ssh keys on your machine you will need to add this line to the vagrant file under the config
|
|
||||||
* Looks somthing like this:
|
|
||||||
Vagrant::Config.run do |config|
|
|
||||||
# stuff
|
|
||||||
config.ssh.forward_agent = true
|
|
||||||
end
|
|
||||||
7. cd into validate_email and run script
|
|
||||||
```
|
|
||||||
$ cd validate_email
|
|
||||||
|
|
||||||
$ python validate_email.py
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
|
@ -17,131 +17,21 @@
|
||||||
# exception of a circular definition (see comments below), and
|
# exception of a circular definition (see comments below), and
|
||||||
# with the omission of the pattern components marked as "obsolete".
|
# with the omission of the pattern components marked as "obsolete".
|
||||||
|
|
||||||
import smtplib
|
|
||||||
import logging
|
|
||||||
import socket
|
|
||||||
|
|
||||||
class ServerError(Exception):
|
from email_regex import email_has_valid_structure
|
||||||
pass
|
from mx_check import mx_check
|
||||||
|
|
||||||
|
|
||||||
|
def validate_email(
|
||||||
|
email_address,
|
||||||
|
check_structure=True,
|
||||||
|
check_mx=True,
|
||||||
|
smtp_timeout=10):
|
||||||
|
|
||||||
MX_DNS_CACHE = {}
|
if check_structure and not email_has_valid_structure(email_address):
|
||||||
MX_CHECK_CACHE = {}
|
|
||||||
|
|
||||||
|
|
||||||
def get_mx_ip(hostname):
|
|
||||||
if hostname not in MX_DNS_CACHE:
|
|
||||||
try:
|
|
||||||
MX_DNS_CACHE[hostname] = DNS.mxlookup(hostname)
|
|
||||||
except ServerError as e:
|
|
||||||
if e.rcode == 3 or e.rcode == 2: # NXDOMAIN (Non-Existent Domain) or SERVFAIL
|
|
||||||
MX_DNS_CACHE[hostname] = None
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
return MX_DNS_CACHE[hostname]
|
|
||||||
|
|
||||||
|
|
||||||
def validate_email(email, check_mx=False, verify=False, debug=False, smtp_timeout=10):
|
|
||||||
"""Indicate whether the given string is a valid email address
|
|
||||||
according to the 'addr-spec' portion of RFC 2822 (see section
|
|
||||||
3.4.1). Parts of the spec that are marked obsolete are *not*
|
|
||||||
included in this test, and certain arcane constructions that
|
|
||||||
depend on circular definitions in the spec may not pass, but in
|
|
||||||
general this should correctly identify any email address likely
|
|
||||||
to be in use as of 2011."""
|
|
||||||
if debug:
|
|
||||||
logger = logging.getLogger('validate_email')
|
|
||||||
logger.setLevel(logging.DEBUG)
|
|
||||||
else:
|
|
||||||
logger = None
|
|
||||||
|
|
||||||
if not email_has_valid_structure(email_address):
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
check_mx |= verify
|
if not check_mx:
|
||||||
if check_mx:
|
return True
|
||||||
if not DNS:
|
|
||||||
raise Exception('For check the mx records or check if the email exists you must '
|
|
||||||
'have installed pyDNS python package')
|
|
||||||
hostname = email[email.find('@') + 1:]
|
|
||||||
mx_hosts = get_mx_ip(hostname)
|
|
||||||
if mx_hosts is None:
|
|
||||||
return False
|
|
||||||
for mx in mx_hosts:
|
|
||||||
try:
|
|
||||||
if not verify and mx[1] in MX_CHECK_CACHE:
|
|
||||||
return MX_CHECK_CACHE[mx[1]]
|
|
||||||
smtp = smtplib.SMTP(timeout=smtp_timeout)
|
|
||||||
smtp.connect(mx[1])
|
|
||||||
MX_CHECK_CACHE[mx[1]] = True
|
|
||||||
if not verify:
|
|
||||||
try:
|
|
||||||
smtp.quit()
|
|
||||||
except smtplib.SMTPServerDisconnected:
|
|
||||||
pass
|
|
||||||
return True
|
|
||||||
status, _ = smtp.helo()
|
|
||||||
if status != 250:
|
|
||||||
smtp.quit()
|
|
||||||
if debug:
|
|
||||||
logger.debug(u'%s answer: %s - %s', mx[1], status, _)
|
|
||||||
continue
|
|
||||||
smtp.mail('')
|
|
||||||
status, _ = smtp.rcpt(email)
|
|
||||||
if status == 250:
|
|
||||||
smtp.quit()
|
|
||||||
return True
|
|
||||||
if debug:
|
|
||||||
logger.debug(u'%s answer: %s - %s', mx[1], status, _)
|
|
||||||
smtp.quit()
|
|
||||||
except smtplib.SMTPServerDisconnected: # Server not permits verify user
|
|
||||||
if debug:
|
|
||||||
logger.debug(u'%s disconected.', mx[1])
|
|
||||||
except smtplib.SMTPConnectError:
|
|
||||||
if debug:
|
|
||||||
logger.debug(u'Unable to connect to %s.', mx[1])
|
|
||||||
return None
|
|
||||||
except AssertionError:
|
|
||||||
return False
|
|
||||||
except (ServerError, socket.error) as e:
|
|
||||||
if debug:
|
|
||||||
logger.debug('ServerError or socket.error exception raised (%s).', e)
|
|
||||||
return None
|
|
||||||
return True
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
return mx_check(email_address)
|
||||||
import time
|
|
||||||
while True:
|
|
||||||
email = raw_input('Enter email for validation: ')
|
|
||||||
|
|
||||||
mx = raw_input('Validate MX record? [yN] ')
|
|
||||||
if mx.strip().lower() == 'y':
|
|
||||||
mx = True
|
|
||||||
else:
|
|
||||||
mx = False
|
|
||||||
|
|
||||||
validate = raw_input('Try to contact server for address validation? [yN] ')
|
|
||||||
if validate.strip().lower() == 'y':
|
|
||||||
validate = True
|
|
||||||
else:
|
|
||||||
validate = False
|
|
||||||
|
|
||||||
logging.basicConfig()
|
|
||||||
|
|
||||||
result = validate_email(email, mx, validate, debug=True, smtp_timeout=1)
|
|
||||||
if result:
|
|
||||||
print("Valid!")
|
|
||||||
elif result is None:
|
|
||||||
print("I'm not sure.")
|
|
||||||
else:
|
|
||||||
print("Invalid!")
|
|
||||||
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
# import sys
|
|
||||||
|
|
||||||
# sys.modules[__name__],sys.modules['validate_email_module'] = validate_email,sys.modules[__name__]
|
|
||||||
# from validate_email_module import *
|
|
||||||
|
|
Loading…
Reference in a new issue