24.6. Проверка почтовых адресов
Не надо быть семи пядей во лбу, чтобы сказать, что происходит в том случае, когда обнаруживается, что адрес является ошибочным. Письмо просто возвращается назад, и это называется "безадресная почта". Представим на секунду Web-узел, который позволяет пользователям заполнять форму, включающую электронный адрес, и отправляет сообщение с благодарностью. Однако в большинстве случаев при вводе адреса может быть допущена ошибка или преднамеренно введены неверные адреса. Конечно, можно проверить форму адреса, но и правильно написанный с точки зрения синтаксиса адрес может содержать ошибки. В таком случае почта возвращается назад отправителю почты. К сожалению, это часто бывает сам Web-сервер.
Просмотр возвращенной почты представляет определенный интерес. Например, те, кто поддерживает коммерческий Web-узел, могут получить информацию о подтверждениях неотправленных заказов. Объемы почты могут быть достаточно большими. Добавим к этому, что ошибка доставки почты может быть растянута во времени. Сама отправка почты может пройти успешно. Уместно будет проверить почтовый адрес перед отправкой почты.
В документе RFC 821 описывается протокол SMTP, используемый для обмена почтой. С ним можно ознакомиться на Web-узле www.faqs.org (<http://www.faqs. org/rfcs/rfc821.html>). Он вполне соответствует своему названию, Simple Mail Transfer Protocol, хотя бы потому, что его довольно просто использовать в сеансе telnet. Для того чтобы проверить адрес, можно подключиться к соответствующему SMTP-серверу и отправить сообщение. Если задан существующий адресат, этот сервер возвратит код 250 , и на этом месте процесс можно прервать.
Выглядит это просто, но здесь есть одна загвоздка. Доменная часть адреса, т.е. часть, следующая после символа @, не обязательно отождествляется с той же машиной, которая получает электронную почту. Домены ассоциируются с одним или несколькими узлами обмена почтой - машинами, принимающими STMP-пакеты для доставки локальной почты. Функция getmxrr возвращает все записи DNS для заданного домена.
А теперь рассмотрим листинг 24.8. Функция verifyEmail основывается на подобной функции, написанной Джоном Стевенсом (Jon Stewens). Как вы видите, эта функция пытается получить выборку узлов обмена почтой. Если домен не имеет активных узлов обмена почтой, сценарий делает вывод, что узел, доменное имя которого указано, сам принимает почту.

Листинг 24.8. Проверка электронного адреса

<html> <head>
^^^^Листинг 24.8</title>
</head>
<body>
<?php
/*
** Функция: verifyEmail
** Вход: STRING address, REFERENCE error ** Выход: BOOLEAN
** Описание: контактируя с узлом обмена почтой, делает 
** попытку проверить почтовый адрес. Зарегистрированные узлы
** обмена почтой сначала запрашиваются у узла управления доменами,
** а затем у самого домена. Аргумент error должен
** содержать соответствующий текст, если адрес невозможно
** проверить.
*/
function verifyEmail($address, &$error) {
$mxhost = array(); $mxweight = array();
list($user, $domain) = split("@", $address, 2); //убедиться в том, что домен имеет узел обмена почтой if(dns_check_record($domain, "MX"))
{
// получить все записи узла обмена почтой if(!dns_get_mx($domain, $mxhost, $mxweight))
{
$error =
"Узлы обмена почтой не обнаружены!^^^"; return(FALSE);
}
}
else
{
//если узел обмена отсутствует, то узел может // сам принимать почту $mxhost[] = $domain; $mxweight[] = 1;
}
//создать отсортированный массив узлов $weighted_host = array(); for($i = 0; $i < count($mxhost);
{
$weighted_host[($mxweight[$i])] = $mxhost[$i];
}
ksort($weighted_host); //пройти каждый узел foreach($weighted_host as $host)
{
// подключиться к узлу через SMTP- порт if(!($fp = fsockopen($host, 25)))
{
//невозможно подключиться к узлу, но //следующий может работать continue;
}
/*
** пропустить сообщения "220"
** завершить работу, если ответа нет в течение 10 секунд */
stream_set_blocking($fp, FALSE); $stopTime = time() + 10; $gotResponse = FALSE; while(TRUE)

{

 //попробовать получить почту с почтового сервера 
$line = fgets($fp, 1024); if(substr($line, 0, 3) == "220")
{
// сбросить таймер $stopTime = time() + 10; $gotResponse = TRUE;
elseif(($line == "") AND ($gotResponse))
break;
elseif(time() > $stopTime) break;
}}
if(!$gotResponse)
{
//этот узел не отвечает, но, //может быть, следующий будет лучше continue;
}
stream_set_blocking($fp, TRUE); // подписать
fputs($fp, "HELO {$_SERVER['SERVER_NAME']}rn");
fgets($fp, 1024);
// задать отправителя
fputs($fp, "MAIL FROM: " .
"<httpd@{$_SERVER['SERVER_NAME']}>rn"); fgets($fp, 1024); // ввести адрес
fputs($fp, "RCPT TO: <$address>rn");
$line = fgets($fp, 1024);
// закрыть соединение
fputs($fp, "QUITrn");
fclose($fp);
if(substr($line, 0, 3) != "250")
{
//почтовый сервер не распознает этот адрес, //поэтому он признается ошибочным $error = $line; return(FALSE);
}
else
{
//адрес распознан return(TRUE);
}
}
$error = "Невозможно достичь узел обмена почтой!"; return(FALSE);
}
if(verifyEmail("leon@clearink.com", $error))
{
print("Проверено!<br>n"),•

else
{
print("Невозможно проверить!<br>n"); print("Ошибка: $error<br>n");
}
?>
</body> </html>