122 lines
3.3 KiB
Python
Executable File
122 lines
3.3 KiB
Python
Executable File
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
|
|
import re
|
|
from email.charset import Charset
|
|
from email.utils import formataddr, parseaddr
|
|
|
|
from trytond.pool import Pool
|
|
|
|
__all__ = [
|
|
'set_from_header', 'validate_email', 'normalize_email',
|
|
'convert_ascii_email', 'EmailNotValidError']
|
|
|
|
|
|
def _domainaddr(address):
|
|
_, email = parseaddr(address)
|
|
if '@' in email:
|
|
return email.split('@', 1)[1]
|
|
|
|
|
|
def set_from_header(message, sender, from_):
|
|
"Fill email headers to appear at best from the address"
|
|
if parseaddr(sender)[1] != parseaddr(from_)[1]:
|
|
if _domainaddr(sender) == _domainaddr(from_):
|
|
message['From'] = from_
|
|
message['Sender'] = sender
|
|
else:
|
|
message['From'] = sender
|
|
message['On-Behalf-Of'] = from_
|
|
message['Reply-To'] = from_
|
|
else:
|
|
message['From'] = from_
|
|
|
|
|
|
def has_rcpt(msg):
|
|
return any((msg['To'], msg['Cc'], msg['Bcc']))
|
|
|
|
|
|
try:
|
|
from dns.exception import DNSException
|
|
from email_validator import EmailNotValidError, caching_resolver
|
|
from email_validator import validate_email as _validate_email
|
|
|
|
try:
|
|
resolver = caching_resolver()
|
|
except DNSException:
|
|
if Pool.test:
|
|
resolver = None
|
|
else:
|
|
raise
|
|
|
|
def validate_email(email):
|
|
emailinfo = _validate_email(
|
|
email, check_deliverability=True,
|
|
dns_resolver=resolver,
|
|
test_environment=Pool.test)
|
|
return emailinfo.normalized
|
|
|
|
def normalize_email(email):
|
|
try:
|
|
emailinfo = _validate_email(
|
|
email, check_deliverability=False,
|
|
test_environment=Pool.test)
|
|
return emailinfo.normalized
|
|
except EmailNotValidError:
|
|
return email
|
|
|
|
def convert_ascii_email(email):
|
|
try:
|
|
emailinfo = _validate_email(
|
|
email, check_deliverability=False,
|
|
test_environment=Pool.test)
|
|
return emailinfo.ascii_email or emailinfo.normalized
|
|
except EmailNotValidError:
|
|
return email
|
|
|
|
except ImportError:
|
|
|
|
def validate_email(email):
|
|
return email
|
|
|
|
def normalize_email(email):
|
|
return email
|
|
|
|
def convert_ascii_email(email):
|
|
return email
|
|
|
|
class EmailNotValidError(Exception):
|
|
pass
|
|
|
|
|
|
# Copy of email.utils.formataddr but without the ASCII enforcement
|
|
specialsre = re.compile(r'[][\\()<>@,:;".]')
|
|
escapesre = re.compile(r'[\\"]')
|
|
|
|
|
|
def _formataddr(pair, charset='utf-8'):
|
|
name, address = pair
|
|
if name:
|
|
try:
|
|
name.encode('ascii')
|
|
except UnicodeEncodeError:
|
|
if isinstance(charset, str):
|
|
charset = Charset(charset)
|
|
encoded_name = charset.header_encode(name)
|
|
return "%s <%s>" % (encoded_name, address)
|
|
else:
|
|
quotes = ''
|
|
if specialsre.search(name):
|
|
quotes = '"'
|
|
name = escapesre.sub(r'\\\g<0>', name)
|
|
return '%s%s%s <%s>' % (quotes, name, quotes, address)
|
|
return address
|
|
|
|
|
|
def format_address(email, name=None):
|
|
pair = (name, convert_ascii_email(email))
|
|
try:
|
|
return formataddr(pair)
|
|
except UnicodeEncodeError:
|
|
return _formataddr(pair)
|