Комментарии 103
Лучший способ валидации для каждого сценария разный.
занудаOFF:
Но вообще я с вами согласен, что для практических целей лучше просто отправить письмо.
Если это допустимо. Часто валидация почты письмом ухудшает конверсию. А проверить при регистрации надо.
Лично я проверяю штатным фильтром пхп, после чего проверяю существование МХ у домена почты. И прочтение статьи меня ничуть не сподвигло проверять насколько строгий фильтр встроен в пхп. Все им пользуются, значит для практических целей это можно условно считать стандартом.
Вообще интересны какие-нибудь исследования про долю ошибок в поле емейла, которые отсекаются чем-то сложнее проверкой наличия коммерческого at и точки, отделяющей TLD.
If no MX records are found, sendmail tries to deliver the message to the single original host.
O'Reilly's book 1997 год
Куда простите старше?)
Я вам больше скажу, есть подозрение, что само понятие MX-ов появилось заметно позже, чем sendmail начал передавать письма по А-записям. Но лень искать подтверждения.
Накидал для примера на коленке, кому будет интересно — [github]/.../tcl-test-valid-mail/tcl/check valid email.tcl
(результат исполнения и лог внизу файла).
Раузмеется, если MTA настроен на обработку подобных случаев. Postfix умеет: у него есть отдельный конфигурационный параметр «разделитель», который по умолчанию (или рекомендуется, я не помню точно) имеет значение "+", и если это включено, то при проверке существования локального адреса user+sub@domain.com он будет делать запросы и для user@domain.com.
Такую фичу имеют многие почтовые сервера, но в этом случае конечно нужно иметь свой домен.
Вообще я сначала на gmail почту поднял для организации, а потом оттуда переехал на яндекс. Ноу проблем. Проблема была только в размере архива писем (>50 гигов, а может и больше, уже точно не помню, даты писем с 2002 года и несколько писем ещё старше были)
Не связано ли это с тем, что в некоторых вариантах SQL двойной дефис обозначает начало комментария? Соответственно, могут его запрещать просто на всякий случай, для защиты от инъекций.
в некоторых вариантах SQL двойной дефис обозначает начало комментария?Ну и пусть себе обозначает. Использовать экранирование опасных последовательностей вместо тупого
exec_sql_query("select * from some_table where some_field ='" + $REQUEST['value_key'] + "'")
разработчикам религия запрещает?Так-то оно так, но попадалась где-то коммерческое программное решение — что-то вроде обратного прокси, кажется — которое пыталось отлавливать "потенциально опасные" в плане инъекций запросы и банило их. Типа "да, ваш бэкенд писали криворукие стажёры, но если вы купите наш продукт, то мы вас спасём от Sql-инъекций, которых у вас наверняка куча".
В качестве приложения приводится выражение, которое проверяет соответствие email RFC, это выражение занимает несколько страниц :)
Хороший был бы емейл вася@пупкин.рф. Упс, а что делать с кодировкой локальной части?
In addition to the above ASCII characters, international characters above U+007F, encoded as UTF-8, are permitted by RFC 6531
Может это мне приснилось, но раньше (до RFC 6531) не было требования UTF-8. Просто не было. Было можно что угодно, за небольшим исключением, и всё. А как это понимать, решает получатель.
Спасибо за подсказку.
И вот здесь, уважаемые коллеги, мы собственными глазами наблюдаем очередное подтверждние тезиса автора статьи о том, что
^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$
У меня в адресе на GMail есть точка. Когда я её регистрировал там в качестве примера имени ящика был ящик вида name.surname@gmail.com.
Знаете, кто меня не пустил регистрироваться с таким адресом? Foursquare! Пришлось пользоваться тем фактом, что точка в имени e-mail игнорируется и писать тот же адрес без точки.
У меня Яндекс с точкой, интересно что кроме точки я могу использовать дефис. Итого в Яндексе количество почтовых адресов увеличилось вдвое.
через которое они все прошли бы. Вот оно.
Коротенькое уж больно. Читайте RFC дальше. filter_var из PHP в исходнике регулярку за килобайт размером имеет.
github.com/php/php-src/blob/PHP-7.0.2/ext/filter/logical_filters.c#L575
Кстати, в комментариях в коду есть ответ на популярный вопрос «какого размера делать поле в БД»: 320 байт. согласно RFC 2821.
tools.ietf.org/html/rfc5321#section-4.5.3
The maximum total length of a user name or other local-part is 64 octets.
The maximum total length of a domain name or number is 255 octets.
Вот они, исходные 320 байт
Но сверху ограничивает RFC 2821, который принимает только 254 октета.
Или, как вариант, с —
<lets make some SQL inj magic>@<lets make some SQL inj magic>
(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:
\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(
?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[
\t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\0
31]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\
](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+
(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:
(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)
?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\
r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[
\t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)
?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t]
)*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[
\t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*
)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)
*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+
|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r
\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t
]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031
]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](
?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?
:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?
:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?
:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?
[ \t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\]
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|
\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>
@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"
(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?
:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[
\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-
\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(
?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;
:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([
^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\"
.\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\
]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\
[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\
r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\]
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]
|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \0
00-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\
.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,
;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?
:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[
^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]
]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)(?:,\s*(
?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(
?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[
\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t
])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t
])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?
:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|
\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:
[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\
]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)
?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["
()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)
?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>
@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[
\t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,
;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:
\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\[
"()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])
*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])
+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\
.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(
?:\r\n)?[ \t])*))*)?;\s*)
Конечно, если речь о лендингах и конверсии, такие заморочки не нужны. Но ведь есть места, где нужна защита от дурака.
- Конкретные реализации регулярных выражений охватывают больше, чем регулярные грамматики,
- На практике можно не рассматривать регулярку как самодостаточный парсер,
- Можно построить такое приближение грамматики, которое будет корректно работать в 99.9% случаев, скажем, реализовать вложенность скобок до какого-то уровня N
А вообще, раз есть ограничение на длину адреса, то количество возможных значений конечно, а значит это просто набор константных строк, которые записываются очень длинной регуляркой вида a@a|a@b|a@c|… :)
А вообще, раз есть ограничение на длину адреса, то количество возможных значений конечно, а значит это просто набор константных строк, которые записываются очень длинной регуляркой вида a@a|a@b|a@c|… :)
Очень очень очень длинной регуляркой...
Подключать терабайты кода для проверки регулярки... Тогда уже пилить отдельный сервис с этим терабайтом специально под проверки адресов..
Я не прав?
Игра Жизнь полна по Тьюрингу, к примеру :-) Что как бы сходу ни разу не очевидно…
Проверьте хотя бы скобочный баланс регуляркой или конечным автоматом — а я над вами посмеюсь.
(([^()]|\((?1)\))*)
Смейтесь на здоровье. С PCRE нам даже КС не страшны:
boost::regex re("(?(DEFINE)"
"(?<symbol>[^()])"
"(?<expr>((?&symbol)|\\((?&expr)\\))*)"
")"
"^(?&expr)$");
А так-то да: PCRE'шные регулярные выражаения это, конечно, умеют, потому что у них есть память (хотя и ограниченное количество памяти, но для скобочек этого хватает, да).
Почему так происходит. Или что будет если я на своем домене таки сделаю нетипичный адрес и начну жаловаться на сервисы которые его не пропускают?
johndoe@gmail.com
john.doe@gmail.com
j.O.h.N.d.O.e@gmail.com
johndoe+sometext@gmail.com
Это всё ещё один ящик. Я часто пользовался комбинацией email+sometext@gmail.com чтобы регистрироваться на разных сервисах, с разными техническими именами ящиков, чтобы потом собирать всю почту в один и рулить фильтрами не по имени отправителя, а по ящику получателя, что ощутимо проще.
Но пару раз столкнулся с тем, что на некоторых сайтах email+sometext зарегистрировать можно, а вот при обращении к нему приходил или invalid email, или полный крах. Пришлось отказаться.
Думаю, я создам email-адрес типа phil.h\@\@ck@haacked.com и начну жаловаться в техподдержку на сайтах, которые требуют ввода email-адреса, но не позволяют мне создать учётную запись с этим адресом. Люблю шалить!
был у меня адрес всецифры@домен — где-то не проходил проверку. жаловался. в ответ игнор. сомневаюсь что шалить будет весело.
Пока не подводил ). Но, не смею претендовать на его незаменимость.
fightingforalostcause.net/content/misc/2006/compare-email-regex.php
user@тест.рф = user@xn--e1aybc.xn--p1ai не пройдёт.
Вдобавок, текст удобрен знатным количеством петросянства, что не способствует восприятию.
it { is_expected.to allow_value("customer\/department=shipping@example.com").for(:email) } it { is_expected.to allow_value("!def!xyz%abc@example.com").for(:email) } it { is_expected.to allow_value("_somename@example.com").for(:email) }
Тесты прошли
Мой валидатор, давно устаревший правда: http://habrahabr.ru/post/175399/
С остальными продолжаю возиться, спасибо за челлендж!
Адрес электронной почты имеет вид
[часть1]@[часть2].[часть3]
Из этого (исключим всякую экзотику типа кириллических доменов):
[часть1] - произвольный текст, состоящий из латинских букв, цифр, точек, дефисов и поддефисов, начинающийся и заканчивающийся на букву или цифру.
[часть2] - произвольный текст, состоящий из латинских букв, цифр, точек и дефисов, содержащий не более одной точки подряд, начинающийся и заканчивающийся на букву или цифру.
[часть3] - домен, который просто проверяется по списку.
Осталось только всего ничего: узаконить это Ваше видение. Потому что в стандарте не так. О чём, собственно, и статья.
А почему бы почте не быть на верхнем домене? Корпы сейчас вполне могут купить себе такой если захотят, например vasya@yandex - вполне себе валидно.
Национальные домены тоже существуют, вася@я.рф - тоже валидно(хотя это больше на совести софта, которые его под капотом закодируют-раскодируют).
В общем валидация почты проста: должен быть символ @ между любых иных, всё остальное не имеет значения.:)
Я знал, как валидировать email-адрес. Пока не прочитал RFC