hobo/hobo/emails/validators.py

65 lines
2.5 KiB
Python

# hobo - portal to configure and deploy applications
# Copyright (C) 2015-2016 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import dns.resolver
import smtplib
import socket
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
def validate_email_address(value):
email_domain = value.split('@')[-1]
try:
mx_server = dns.resolver.query(email_domain, 'MX')[0].exchange.to_text()
except dns.resolver.NXDOMAIN as e:
raise ValidationError(_('Error: %s') % str(e))
except dns.resolver.NoAnswer as e:
raise ValidationError(_('Error: %s') % str(e))
smtp = smtplib.SMTP(timeout=30)
try:
smtp.connect(mx_server)
except socket.error as e:
raise ValidationError(_('Error while connecting to %(server)s: %(msg)s') % {'server': mx_server, 'msg': str(e)})
status, msg = smtp.helo()
if status != 250:
smtp.quit()
raise ValidationError(_('Error while connecting to %(server)s: %(msg)s') % {'server': mx_server, 'msg': msg})
smtp.mail('')
status, msg = smtp.rcpt(value)
if status == 250:
smtp.quit()
return
smtp.quit()
raise ValidationError(_('Email address not found on %s') % mx_server)
def validate_email_spf(value, strict=False):
allowed_records = settings.ALLOWED_SPF_RECORDS
email_domain = value.split('@')[-1]
txt_records = sum([r.strings for r in dns.resolver.query(email_domain, 'TXT')], [])
spf_records = [x for x in txt_records if x.startswith('v=spf1 ')]
if not strict and not spf_records:
return
for spf_record in spf_records:
if '+all' in spf_record:
return
for allowed_record in allowed_records:
if allowed_record in spf_record:
return
raise ValidationError(_('No suitable SPF record found for %s') % email_domain)