diff --git a/mo/users.py b/mo/users.py
index b57c6e0557861c16c31f7ac9afb63089d2d94aeb..e765fe2f5366c20d0b778c54568b8aea8b9c44e1 100644
--- a/mo/users.py
+++ b/mo/users.py
@@ -3,6 +3,8 @@
import bcrypt
import datetime
import dateutil.tz
+import dns.exception
+import dns.resolver
import email.errors
import email.headerregistry
import re
@@ -246,7 +248,44 @@ def find_or_create_participation(user: db.User, contest: db.Contest, place: Opti
return pion, is_new
-def normalize_email(addr: str) -> str:
+def email_is_fake(addr: str) -> bool:
+ return addr.endswith('@nomail') or addr.endswith('@test')
+
+
+bad_domains = {
+ 'gmail.cz',
+}
+
+
+def email_check_domain(domain: str):
+ # Některé domény rovnou odmítáme
+ if domain in bad_domains:
+ logger.info(f'DNS: Doména <{domain}> na blacklistu')
+ raise mo.CheckError('Doména {domain} nepřijímá poštu')
+
+ for record in ['MX', 'A', 'AAAA']:
+ try:
+ dns.resolver.resolve(domain, record, lifetime=2, search=False)
+ except dns.exception.Timeout:
+ # Kontrola je konzervativní, při timeoutu adresu raději schválíme
+ logger.info(f'DNS: Timeout při kontrole domény <{domain}>')
+ return
+ except dns.resolver.NoAnswer:
+ logger.debug(f'DNS: Doména <{domain}> neobsahuje {record}')
+ except dns.resolver.NXDOMAIN:
+ logger.info(f'DNS: Doména <{domain}> neexistuje')
+ raise mo.CheckError('Adresa obsahuje neexistující doménu {domain}')
+ except dns.exception.DNSException as e:
+ logger.warn(f'DNS: Záznam <{domain}>/{record} nejde resolvovat: {e}')
+ return
+ logger.debug(f'DNS: Doména <{domain}> OK')
+ return
+
+ logger.info(f'DNS: Doména <{domain}> nepřijímá poštu')
+ raise mo.CheckError('Doména {domain} nepřijímá poštu')
+
+
+def normalize_email(addr: str, check_existence: bool = False) -> str:
if not re.fullmatch(r'.+@.+', addr):
raise mo.CheckError('V e-mailové adrese chybí zavináč')
@@ -263,10 +302,16 @@ def normalize_email(addr: str) -> str:
try:
# Tady úmyslně používáme knihovnu jen ke kontrole a ne k normalizaci,
# protože nechceme riskovat, že se normalizovaný tvar časem změní.
- email.headerregistry.Address(addr_spec=addr)
+ addr_obj = email.headerregistry.Address(addr_spec=addr)
except (email.errors.HeaderParseError, ValueError):
raise mo.CheckError('Chybná syntaxe mailové adresy')
+ if addr_obj.display_name != "":
+ raise mo.CheckError('Chybná syntaxe mailové adresy')
+
+ if check_existence and not email_is_fake(addr):
+ email_check_domain(addr_obj.domain)
+
# XXX: Striktně vzato, tohle není korektní, protože některé domény mohou
# mít case-sensitive levou stranu adresy. Ale i na nich se prakticky nevyskytují
# levé strany s velkými písmeny, zatímco uživatelé při psaní adres běžně velká