Reorganised code; mx check in separate file

This commit is contained in:
Ben Baert 2018-05-31 14:58:35 +02:00
parent 9f9fc94875
commit a110914328
8 changed files with 92 additions and 199 deletions

View file

@ -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>

View file

@ -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

View file

@ -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

View file

@ -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
View 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

View file

@ -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))

View file

@ -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
```

View file

@ -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 *