From 42059a968bfdc3aa7bb115a35aa210105ce407ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AE=D1=80=D0=B8=D0=B9=20=D0=9F=D0=B0=D0=B9=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= Date: Tue, 24 May 2016 17:38:13 +0500 Subject: [PATCH 1/3] Added options for MAIL FROM: statememt --- validate_email.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/validate_email.py b/validate_email.py index 0f18e3e..7836ec4 100644 --- a/validate_email.py +++ b/validate_email.py @@ -109,7 +109,7 @@ def get_mx_ip(hostname): return MX_DNS_CACHE[hostname] -def validate_email(email, check_mx=False, verify=False, debug=False, smtp_timeout=10): +def validate_email(email, check_mx=False, verify=False, debug=False, mail_from='', 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* @@ -153,7 +153,14 @@ def validate_email(email, check_mx=False, verify=False, debug=False, smtp_timeou if debug: logger.debug(u'%s answer: %s - %s', mx[1], status, _) continue - smtp.mail('') + + status, _ = smtp.mail(mail_from) + if status != 250: + if debug: + logger.debug(u'%s answer: %s - %s', mx[1], status, _) + smtp.quit() + continue + status, _ = smtp.rcpt(email) if status == 250: smtp.quit() @@ -193,9 +200,11 @@ def validate_email(email, check_mx=False, verify=False, debug=False, smtp_timeou else: validate = False + sender_address = raw_input('Use sender address? [] ').strip() + logging.basicConfig() - result = validate_email(email, mx, validate, debug=True, smtp_timeout=1) + result = validate_email(email, mx, validate, debug=True, mail_from=sender_address, smtp_timeout=1) if result: print("Valid!") elif result is None: From 59c9dc3049fd08bdc3388e7278741c5e3f0f6f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AE=D1=80=D0=B8=D0=B9=20=D0=9F=D0=B0=D0=B9=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= Date: Tue, 24 May 2016 18:11:11 +0500 Subject: [PATCH 2/3] Don't be unsure --- validate_email.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validate_email.py b/validate_email.py index 7836ec4..89a5f0b 100644 --- a/validate_email.py +++ b/validate_email.py @@ -174,7 +174,7 @@ def validate_email(email, check_mx=False, verify=False, debug=False, mail_from=' except smtplib.SMTPConnectError: if debug: logger.debug(u'Unable to connect to %s.', mx[1]) - return None + return False except AssertionError: return False except (ServerError, socket.error) as e: From 771413f745e4897dcfa1fbc4109a2a09b694fd0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AE=D1=80=D0=B8=D0=B9=20=D0=9F=D0=B0=D0=B9=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= Date: Tue, 24 May 2016 19:00:22 +0500 Subject: [PATCH 3/3] Slight refactoring --- validate_email.py | 62 ++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/validate_email.py b/validate_email.py index 89a5f0b..1bf8007 100644 --- a/validate_email.py +++ b/validate_email.py @@ -95,6 +95,7 @@ class ServerError(Exception): MX_DNS_CACHE = {} MX_CHECK_CACHE = {} +logger = logging.getLogger('validate_email') def get_mx_ip(hostname): if hostname not in MX_DNS_CACHE: @@ -109,6 +110,24 @@ def get_mx_ip(hostname): return MX_DNS_CACHE[hostname] +def check_command(result_tuple, server_name='server', ok_codes=None, debug=False): + status, mes = result_tuple + if not ok_codes: + ok_codes = [250] + if status not in ok_codes: + if debug: + logger.debug(u'%s answer: %s - %s', server_name, status, mes) + return False + return True + + +def check_command_for_server(server_name): + def wrapper(*args, **kwargs): + kwargs['server_name'] = server_name + return check_command(*args, **kwargs) + return wrapper + + def validate_email(email, check_mx=False, verify=False, debug=False, mail_from='', smtp_timeout=10): """Indicate whether the given string is a valid email address according to the 'addr-spec' portion of RFC 2822 (see section @@ -118,10 +137,7 @@ def validate_email(email, check_mx=False, verify=False, debug=False, mail_from=' 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 try: assert re.match(VALID_ADDRESS_REGEXP, email) is not None @@ -136,44 +152,36 @@ def validate_email(email, check_mx=False, verify=False, debug=False, mail_from=' return False for mx in mx_hosts: try: - if not verify and mx[1] in MX_CHECK_CACHE: - return MX_CHECK_CACHE[mx[1]] + server_name = mx[1] + check = check_command_for_server(server_name) + if not verify and server_name in MX_CHECK_CACHE: + return MX_CHECK_CACHE[server_name] smtp = smtplib.SMTP(timeout=smtp_timeout) - smtp.connect(mx[1]) - MX_CHECK_CACHE[mx[1]] = True + smtp.connect(server_name) + MX_CHECK_CACHE[server_name] = 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, _) + + if not check(smtp.helo()): continue - status, _ = smtp.mail(mail_from) - if status != 250: - if debug: - logger.debug(u'%s answer: %s - %s', mx[1], status, _) - smtp.quit() + if not check(smtp.mail(mail_from)): continue - status, _ = smtp.rcpt(email) - if status == 250: - smtp.quit() + if check(smtp.rcpt(email)): 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]) + finally: + try: + smtp.quit() + except smtplib.SMTPServerDisconnected: + pass + return False except AssertionError: return False