Регистрация Войти
Войти через VK Войти через FB Войти через Google Войти через Яндекс
Войти через VK Войти через FB Войти через Google Войти через Яндекс
Поиск по сайту
Отправка почты по шаблону минуя SMTP-сервер провайдера на PHP
Пример по созданию и отправке письма на основании активного шаблона письма минуя SMTP-сервер провайдера.
// Посылка почты вручную, минуя SMTP-сервер провайдера.
include_once "lib/getmxrr.php";
include_once "lib/template.php";
include_once "lib/mailenc.php";
include_once "lib/mailx.php";
include_once "lib/socketmail.php";
$mail = template("mail.php.eml", array(
"to" => "Кто-то очень страшный <somebody@thematrix.com>",
"text" => "Проверка слуха!"
));
$mail = mailenc($mail);
mailx_manual($mail, $reason)
or die("Errors:<br>".join("<br>", $reason));
echo "Почта отослана.";
lib/getmxrr.php - эмуляция функции getmxrr(), которая не работает на Windows хостинге
if (!function_exists("getmxrr")) {
function getmxrr($hostname, &$hosts, &$weights=false) {
$hosts = $weights = [];
// Не идеальный способ, но работающий: используется внешняя
// программа nslookup, доступная в WIndows NT/2000/XP/2003.
exec("nslookup -type=mx $hostname", $result);
// Построчно перебираем ответ утилиты.
foreach ($result as $line) {
// Выделяем имя почтового сервера.
if (preg_match('/mail\s+exchanger\s*=\s*(\S+)/', $line, $pock)) {
$hosts[] = $pock[1];
// Также выделяем вес.
if (preg_match("/MX\s+preference\s*=\s*(\d+)/", $line, $pock))
$weights[] = $pock[1];
else
$weights[] = 0;
}
}
return count($hosts) > 0;
}
}
// В PHP5 появился синоним для getmxrr() - его мы тоже эмулируем.
if (!function_exists("dns_get_mx")) {
function dns_get_mx($hostname, &$hosts, &$weights) {
return getmxrr($hostname, $hosts, $weights);
}
}
lib/template.php - обработка шаблона
// Функция делает то же самое, что инструкция include, однако
// блокирует вывод текста в браузер. Вместо этого текст возвращается
// в качестве результата. Функцию можно использовать, например,
// для обработки почтовых шаблонов.
function template($__fname, $vars) {
// Перехватываем выходной поток.
ob_start();
// Запускаем файл как программу на PHP.
extract($vars, EXTR_OVERWRITE);
include($__fname);
// Получаем перехваченный текст.
$text = ob_get_contents();
ob_end_clean();
return $text;
}
lib/mailx.php
// Функция отправляет письмо, полностью заданное в параметре $mail.
// Корректно обрабатываются заголовки To и Subject.
function mailx($mail) {
// Разделяем тело сообщения и заголовки.
list ($head, $body) = preg_split("/\r?\n\r?\n/s", $mail, 2);
// Выделяем заголовок To.
$to = "";
if (preg_match('/^To:\s*([^\r\n]*)[\r\n]*/m', $head, $p)) {
$to = @$p[1]; // сохраняем
$head = str_replace($p[0], "", $head); // удаляем из исходной строки
}
// Выделяем Subject.
$subject = "";
if (preg_match('/^Subject:\s*([^\r\n]*)[\r\n]*/m', $head, $p)) {
$subject = @$p[1];
$head = str_replace($p[0], "", $head);
}
// Отправляем почту. Внимание! Опасный прием!
mail($to, $subject, $body, trim($head));
}
lib/mailenc.php
// Корректно кодирует все заголовки в письме $mail с использованием
// метода base64. Кодировка письма определяется автоматически на основе
// заголовка Content-type. Возвращает полученное письмо.
function mailenc($mail) {
// Разделяем тело сообщения и заголовки.
list ($head, $body) = preg_split("/\r?\n\r?\n/s", $mail, 2);
// Определяем кодировку письма по заголовку Content-type.
$encoding = '';
$re = '/^Content-type:\s*\S+\s*;\s*charset\s*=\s*(\S+)/mi';
if (preg_match($re, $head, $p)) $encoding = $p[1];
// Проходимся по всем строкам-заголовкам.
$newhead = "";
foreach (preg_split('/\r?\n/s', $head) as $line) {
// Кодируем очередной заголовок.
$line = mailenc_header($line, $encoding);
$newhead .= "$line\r\n";
}
// Формируем окончательный результат.
return "$newhead\r\n$body";
}
// Кодирует в строке максимально возможную последовательность
// символов, начинающуюся с недопустимого символа и НЕ
// включающую E-mail (адреса E-mail обрамляют символами < и >).
// Если в строке нет ни одного недопустимого символа, преобразование
// не производится.
function mailenc_header($header, $encoding) {
// Кодировка не задана - делать нечего.
if (!$encoding) return $header;
// Сохраняем кодировку в глобальной переменной. Без использования
// ООП это - единственный способ передать дополнительный параметр
// callback-функции.
$GLOBALS['mail_enc_header_encoding'] = $encoding;
return preg_replace_callback(
'/([\x7F-\xFF][^<>\r\n]*)/s',
'mailenc_header_callback',
$header
);
}
// Служебная функция для использования в preg_replace_callback().
function mailenc_header_callback($p) {
$encoding = $GLOBALS['mail_enc_header_encoding'];
// Пробелы в конце оставляем незакодированными.
preg_match('/^(.*?)(\s*)$/s', $p[1], $sp);
return "=?$encoding?B?".base64_encode($sp[1])."?=".$sp[2];
}
lib/socketmail.php
// Функция для посылки почты вручную, минуя SMTP-сервер провайдера.
// Использует getmxrr(), которая в Windows не поддерживается.
function mailx_manual($mail, &$reason=false) {
$reason = [];
// Выделяем заголовок To.
$to = "";
if (preg_match('/^To:\s*([^\r\n]*)[\r\n]*/m', $mail, $p)) {
$to = @$p[1]; // сохраняем
}
// Вначале выделяем имя хоста.
if (!preg_match('/@([\w.-]*)/', $to, $pockets)) {
$reason[] = "invalid e-mail address - $to: no host";
return false;
}
$host = $pockets[1];
// По стандарту SMTP, если нет ни одной MX-записи, то в качестве
// почтового сервера выступает сам хост.
if (!getmxrr($host, $mxes, &$weights)) {
$mxes = array($host);
}
// Проходимся по всем MX-записям. Для простоты веса не
// учитываем - большой беды от этого не будет.
foreach ($mxes as $mx) {
$result = socketmail($mx, $mail, $r);
$reason = array_merge($reason, $r);
// Если письмо отослано, все сделано.
if ($result) return true;
}
$reason[] = "could not connect to any of (".join(", ", $mxes).")";
return false;
}
// Функция открывает соединение с SMTP-сервером $host
// и пытается отправить через него письма, заданные в $mails.
// Все ошибки накапливаются в необязательной переменной $reason
// в виде списка строк. В случае, если отправка всех писем
// прошла успешно, функция возвращает true, иначе - false.
// Каждое письмо в $mails должно быть представлено в формате:
// "Заголовки\r\n\r\nТело". Функция не заботится о правильном
// кодировании заголовков, предполагая, что это уже было
// сделано ранее.
function socketmail($host, $mails, &$reason=false) {
// Открываем соединение с почтовым сервером.
$reason = [];
$f = @fsockopen($host, 25, $errno, $errstr, 30);
if (!$f) {
$reason[] = "could not open $host:25";
return false;
}
$answer = fgets($f, 1024);
// Сигнализируем о начале обмена данными.
fputs($f, "HELO {$_SERVER['SERVER_NAME']}\r\n");
$answer = fgets($f, 1024);
// Отправляем все письма для этого сервера в цикле.
// Все это происходит за одно соединение с сервером.
if (!is_array($mails)) $mails = array($mails);
foreach ($mails as $i=>$data) {
// Получаем заголовки и тело сообщения.
list ($headers, $body) = preg_split('/\r?\n\r?\n/', $data, 2);
// Получаем заголовок From.
if (!preg_match('/^From:\s*(.*)/mi', $headers, $pockets)) {
$reason[] = "could not find required From: header in mail #$i";
continue;
}
$from = getEmail($pockets[1]);
if (!$from) {
$reason[] = "no email in From: header in mail #$i";
continue;
}
// Получаем заголовок To.
if (!preg_match('/^To:\s*(.*)/mi', $headers, $pockets)) {
$reason[] = "could not find required To: header in mail #$i";
continue;
}
$to = getEmail($pockets[1]);
if (!$to) {
$reason[] = "no email in To: header in mail #$i";
continue;
}
// Т.к. точка в протоколе SMTP свидетельствует о конце данных,
// (см. ниже), мы ее удваиваем.
$data = preg_replace("/\n\./", "\n..", $data);
// Отправляем управляющие команды SMTP.
do {
if (!smtp_say($f, "MAIL FROM: <$from>", $reason)) break;
if (!smtp_say($f, "RCPT TO: <$to>", $reason)) break;
if (!smtp_say($f, "DATA", $reason)) break;
// Печатаем данные.
fputs($f, trim($data)."\r\n");
if (!smtp_say($f, ".", $reason)) break;
} while (false);
// Конец письма.
!smtp_say($f, "RSET", $reason);
}
// Говорим серверу об окончании работы.
@smtp_say($f, "QUIT", $reason);
fclose($f);
return !count($reason);
}
function smtp_say($f, $cmd, &$reason) {
fputs($f, "$cmd\r\n");
$answer = fgets($f, 1024);
# echo "> $cmd<br>< $answer<br>";
if (!preg_match('/^(250|354|221)/', $answer)) { $reason[] = "$answer"; return; }
return true;
}
// Извлекает первый адрес E-mail из заголовка To или From.
// Внимание: упрощенная версия!
function getEmail($header) {
if (!preg_match('/([\w.-]+@[\w.-]+)/s', $header, $p)) return;
return $p[1];
}
mail.php.eml - активный шаблон письма
From: Почтовый робот <somebody@mail.ru>
To: <?=$to?>
Subject: Добрый день!
Content-type: text/plain; charset=windows-1251
Привет, <?=$to?>!
<?=$text?>
Содержимое переменных окружения на момент отправки письма:
<?print_r($_SERVER)?>
Это сообщение сгенерировано роботом - не отвечайте на него.
Здесь представлен полностью работающий скрипт, Вы можете собрать его сами в одну папку, поправить адреса и запустить.
Всего за 990 рублей (~11$) Вы можете приобрести готовый rar-архив, содержащий скрипт, который также поддерживает весь функционал, и имеет дополнительные отладочные опции. Код скрипта реализован на PHP, полностью открытый и не использует никаких дополнительных библиотек.
Соглашение по использованию платной версии:
- Вы можете использовать полученный код в любых своих разработках, вы не обязаны указывать ссылку на источник.
- Вы НЕ имеете права перепродавать её, размещать в свободном или ограниченном доступе, а также публиковать в любом виде.
- Все остальные права сохраняются за автором.
Регистрация Войти Войти через VK Войти через FB Войти через Google Войти через Яндекс
.
Прокомментировать/Отблагодарить