Назад на главную
Отправка писем через SMTP с сайта

Расширения для популярных CMS

Готовый SMTP скрипт для отправки сообщений с сайта

Альтернативный мануал по настройке php smtp скрипта

Отправка почты вручную.
Сначала немного разберем протокол SMTP, и попробуем отправить почту с помощью командной строки винды, чтобы понять принцип общения с smtp сервером. Для этого потребуется встроенная утилита telnet. Во всех виндовсах она есть, но в windows Vista по умолчанию она выключена. Чтобы ее включить зайдите впанель управления/программы/включение или отключение компонентов windows и отметьте галочку возле Клиент Telnet и жмите OK. Включение может длиться до 10 минут. Теперь идем дальше.
Тестировать вам придется на своем smtp сервере, надеюсь у каждого есть почта на бесплатных серверах, вот на них и можете потестировать.
Для примера буду использовать mail.ru
Запускаем командную строку(ПУСК/выполнить/cmd/[OK])
В открывшемся окне пишем

telnet smtp.mail.ru 25 <жмем inter>

Если соединение прошло нормально, сервер должен ответить примерно так

220 mail.ru ESMTP Sat, 11 Aug 2007 17:32:14 +0400

Теперь здороваемся с сервером, представляемся. Обычно после EHLO указывается имя вашего компьютера, можете указать любое произвольное имя

EHLO vasya

Если все ок, ответ будет

250-mx30.mail.ru Hello mail.ru [80.64.80.192]
250-SIZE 10485760
250-8BITMIME
250-AUTH PLAIN LOGIN
250 PIPELINING

После подобного ответа можно вводить логин и пароль для авторизации. Для этого вводим команду.

AUTH LOGIN

получаем ответ

334 VXNlcm5hbWU6

И вот сейчас надо вводить пароль логин, но они должны быть закодированы.
Самый простой способ закодировать логин и пароль, это при помощи функции php base64_encode()
Создайте следующий php код и запустите его на тестовом сервере.

<?
print base64_encode("логин");
print '<br>';
print base64_encode("пароль");
?>

Коды логина и пароля получены, теперь можно ввести их.
Копируйте и вставляйте их по очереди.
После ввода логина должно появиться сообщение с кодом 334.
После ввода правильного пароля должно появиться

235 Authentication succeeded

Авторизация пройдена. Сейчас указываем от кого будет написано письмо и размер.
Указываете свой ящик, от имени которого авторизовались и обязательно в таком формате <email>
Размер письма указывается в байтах учитывая все заголовки, тело письма и вложенные файлы. 1 символ = 1 байт.

MAIL FROM:<login@mail.ru> SIZE=208

Если сервер принял этот адрес, получите ответ

250 OK

Теперь указываем email получателя, тоже окружая его < >.

RCPT TO:<asd@qwe.ru>

положительный ответ сервера

250 Accepted

Если нужно письмо отправить нескольким адресатам, повторяем команду RCPT TO: сколько нужно раз. Будет разослано одно письмо многим адресатам.
А сейчас настало время самого письма. Вводим команду

DATA

Ответ будет примерно таким

354 Enter message, ending with "." on a line by itself

Сейчас можно вводить текст письма.
Само письмо состоит из заголовков и тела.
Заголовки конечно можно не писать, но лучше чтобы они были
Заголовки от тела отделяются пустой строкой
Кстати, вот весь этот текст(заголовки и тело) учитываются при подсчете размера в команде «MAIL FROM:<login@mail.ru> SIZE=208«

Subject: Это тема письма
To: asd@qwe.ru
X-Mailer: webi.ru mailer

Отделили заголовки пустой строкой, и теперь пишем текст письма…
А чтобы закончить ввод письма, нужно на отдельной строке ввести точку.

.

Когда введете точку, получите такой ответ

250 OK id=1IiR72-000ONs-00

Теперь завершаем работу с сервером.

QUIT

ответ

221 mx30.mail.ru closing connection

Письмо отправлено. Учтите, при отправке письма из командной строки windows, скорее всего оно будет отправлено в кодировке DOS (cp866), поэтому в заголовки письма нужно добавить заголовок кодировки, чтобы письмо нормально читалось. Но данный пример я показал лишь для демонстрации принципа общения с SMTP сервером.
Полное описание протокола smtp, все команды и коды ответов сервера читайте в документе PDF на русском языке протокол SMTP

Отправка через PHP.
А сейчас все это общение с сервером переведем на php.
После каждой передачи команды на сервер, нужно будет получать ответ от сервера.
Для этого сделаем маленькую функцию, эта функция будет работать с открытым соединением и получать ответ от сервера.

<?
function get_data($smtp_conn)
{
  $data="";
  while($str = fgets($smtp_conn,515))
  {
    $data .= $str;
    if(substr($str,3,1) == " ") { break; }
  }
  return $data;
}
?>

Дальше, сразу создадим необходимые заголовки.
Привожу пример классических заголовков, на примере почтовика The bat.
Это письмо не будет отличаться от отправленного через этот почтовик.

<?
$header="Date: ".date("D, j M Y G:i:s")." +0700\r\n";
$header.="From: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Максим')))."?= <login@mail.ru>\r\n";
$header.="X-Mailer: The Bat! (v3.99.3) Professional\r\n";
$header.="Reply-To: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Максим')))."?= <login@mail.ru>\r\n";
$header.="X-Priority: 3 (Normal)\r\n";
$header.="Message-ID: <172562218.".date("YmjHis")."@mail.ru>\r\n";
$header.="To: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Сергей')))."?= <asd@qwe.ru>\r\n";
$header.="Subject: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('проверка')))."?=\r\n";
$header.="MIME-Version: 1.0\r\n";
$header.="Content-Type: text/plain; charset=windows-1251\r\n";
$header.="Content-Transfer-Encoding: 8bit\r\n";
?>

Переменную с заголовками создали, теперь создадим переменную с текстом самого письма…

$text="привет, проверка связи.";

А сейчас открываем соединение с smtp сервером.

$smtp_conn = fsockopen("smtp.mail.ru", 25,$errno, $errstr, 10);

После открытия соединения читаем ответ от сервера в переменную $data

$data = get_data($smtp_conn);

Ну а теперь начинаем запускать все те команды, которые вводили руками до этого.
И после каждого ввода команды считываем ответ от сервера…

<?
fputs($smtp_conn,"EHLO vasya\r\n");
$data = get_data($smtp_conn);

fputs($smtp_conn,»AUTH LOGIN\r\n»);
$data = get_data($smtp_conn);

fputs($smtp_conn,base64_encode(«login»).»\r\n»);
$data = get_data($smtp_conn);

fputs($smtp_conn,base64_encode(«password»).»\r\n»);
$data = get_data($smtp_conn);

// считаем количество символов письма со всеми заголовками, чтобы передать какого размера будет письмо
$size_msg=strlen($header.»\r\n».$text);

fputs($smtp_conn,»MAIL FROM:<login@mail.ru> SIZE=».$size_msg.»\r\n»);
$data = get_data($smtp_conn);

fputs($smtp_conn,»RCPT TO:<asd@qwe.ru>\r\n»);
$data = get_data($smtp_conn);

fputs($smtp_conn,»DATA\r\n»);
$data = get_data($smtp_conn);

fputs($smtp_conn,$header.»\r\n».$text.»\r\n.\r\n»);
$data = get_data($smtp_conn);

fputs($smtp_conn,»QUIT\r\n»);
$data = get_data($smtp_conn);
?>

Вот и улетело письмишко.

Для чего же читать ответ от сервера после каждой команды?
Как видно, после каждой команды сервер отвечает кодом из трех цифр и текстовым пояснением.
И после каждого ввода команды нужно знать, принял сервер команду или нет.
Выдернуть код очень просто, надо просто получить первые три символа строки

$code = substr($data,0,3);

Какие коды должен выдавать сервер и на какие команды, читайте в спецификации протокола smtp

Отправка нескольких писем за одно соединение.
Если вы хотите отправить одно письмо многим адресатам, то вам достаточно использовать команду RCPT TO несколько раз подряд, указывая в ней email адреса, на которые хотите разослать письмо. После этого как и положено идет DATA и дальше письмо… И ваше письмо разошлется по всем указанным адресам.
Если хотите отправить несколько разных писем, разным адресатам, тогда делаем так:
Сначала идет все стандартно, соеденились, авторизовались, отправили письмо и после отправки письма посылаем команду не выход, а сброс RSET.
Эта команда сбрасывает все, что было введенно в текущем сеансе и после этого можно начинать формировать и отправлять следующее письмо.
Начинаем отправлять снова
MAIL FROM:
…….
RCPT TO:……
и т.д.
То есть формируем с самого начала новое письмо…. После завершения отправки можно снова ввести RSET и сформировать третье письмо, четвертое и т.д.
Ну и в самом конце уже ставим QUIT.

Не много о заголовках.
Date: — дата написания(отправки) письма
X-Mailer: — почтовая программа, которая отправляла письмо
X-Priority: приоритет
From: от кого
Reply-To: куда писать ответ, при нажатии на кнопку ОТВЕТИТЬ
To: кому
Subject: тема

В полях From, Reply-To, To можно указывать не только email, но и имя (Максим <login@mail.ru>).
Вот только при написании имен русскими символами, придется их конвертировать
=?windows-1251?Q?».str_replace(«+»,»_»,str_replace(«%»,»=»,urlencode(‘Максим’))).»?= <login@mail.ru>
По такому же принципу кодируется и тема…

Еще одно интересное поле Message-ID:
Это идентификатор сообщения. Должен быть уникальным в пределах вселенной.
В примере я использовал принцип создания идентификатора почтовика The bat.
Message-ID: <172562218.».date(«YmjHis»).»@mail.ru>
после преобразования получится примерно так
Message-ID: <172562218.2007081310152@mail.ru>
Каждые почтовики создают это поле по-своему. The bat создает именно так.
Первая часть до точки это должно быть случайным числом, вторая часть после точки это дата без разделителей.
Поле Message-ID не является обязательным, а в некоторых случаях сервер сам подставляет это поле.
Но лучше его использовать, так как некоторые фильтры антиспама проверяют это поле. И если вы указали в заголовках, что письмо было написано якобы через The bat, то и Message-ID должен быть сгенерирован по правилам этого почтовика.
Можете конечно использовать свое имя почтовика, например
X-Mailer: webi.ru (v2.32.3)
Но сильно тупые фильтры антиспама будут принимать вашу почту за спам.

Прикрепление файлов к письму.
На предыдущем примере вложим два файла.
Для этого нужно внести поправку в заголовки.
Content-Type: text/plain; charset=windows-1251
Content-Transfer-Encoding: 8bit
Заменяем вот эти два заголовка на
Content-Type: multipart/mixed; boundary=»———-A4D921C2D10D7DB»
Этот заголовок означает, что в письме будут разные типы данных, и разделитель между этими данными будет «———-A4D921C2D10D7DB».
Разделитель может быть любым, на ваше усмотрение, главное правило, чтобы разделитель(метка) начинался с «—» и чтобы такая последовательность символов не встречалась в тексте письма.

Файлы будут вставляться в тело письмо в перекодированном виде в base64.
Подготовим две текстовые переменные, содержащие файлы в кодированном виде.

<?
$file="path/1.jpg";
$fp = fopen($file, "rb");
$code_file1 = chunk_split(base64_encode(fread($fp, filesize($file))));
fclose($fp);
?>

Обратите внимание, как читается и кодируется файл. Вместе с кодированием он разбивается на строки по 76 символов, чтобы не было длинной строки, так как длина строки в почтовом формате ограничена.
Таким образом, можно вставить не только jpg, но и любой бинарный файл.
Итак, один файл готов к вложению и сейчас создадим еще один файл, текстовый. Текстовый файл необязательно читать с диска, можно создать его сразу из текста.

$code_file2=base64_encode("привет, это типа второй файл");
Вот таким образом создали вторую текстовую переменную, которая содержит текстовые данные в base64.

И теперь можно редактировать переменную $text, которая у нас является телом письма.


<?
$text="------------A4D921C2D10D7DB
Content-Type: text/plain; charset=windows-1251
Content-Transfer-Encoding: 8bit

привет, это текст письма

————A4D921C2D10D7DB
Content-Type: application/octet-stream; name=\»1.jpg\»
Content-transfer-encoding: base64
Content-Disposition: attachment; filename=\»1.jpg\»

«.$code_file1.»
————A4D921C2D10D7DB
Content-Type: application/octet-stream; name=\»2.txt\»
Content-transfer-encoding: base64
Content-Disposition: attachment; filename=\»2.txt\»

«.$code_file2.»
————A4D921C2D10D7DB—
«;
?>

Сначала ставим разделитель, указывая, что сейчас пойдет первая часть письма и после разделителя указываем какой тип данных будет использован.
Content-Type: text/plain; charset=windows-1251
Content-Transfer-Encoding: 8bit
Здесь указано, что сейчас пойдет обычный текст. Обязательно заголовки нужно отделить пустой строкой, пустая строка признак того, что заголовок кончился и пошло тело…

Когда надо начать вставлять вложения, нужно поставить разделитель(метку), это будет признаком, что одна часть кончилась и начинается следующая.

————A4D921C2D10D7DB
Content-Type: application/octet-stream; name=»1.jpg»
Content-transfer-encoding: base64
Content-Disposition: attachment; filename=»1.jpg»

Вот поставлен разделитель, значит началась вторая часть.
Так же надо указать какие данные будут в этой части.
Данные заголовки показывают, что сейчас пойдет кодированный в base64 файл с именем 1.jpg
После заголовков ставим пустую строку и вставляем кодированный текст $code_file1
Потом опять ставим разделитель и снова указываем заголовки, что пойдет еще один файл. И т.д. Так можно вкладывать файлы еще и еще….
Когда вложения закончены, нужно завершить это тем же самым разделителем, только в конце должно быть «—«, это будет признаком окончания вложений.
Ну а дальше, как обычно, вставляем это все в отправку на сервер

fputs($smtp_conn,$header."\r\n".$text."\r\n.\r\n");

Еще раз уточню, разделитель «————A4D921C2D10D7DB» я создал случайным образом, он может быть любой по вашему усмотрению, главное, он должен начинаться с «—» и не встречаться в самом письме…


А сейчас полные примеры без комментариев…

Отправка письма без вложений

<?
function get_data($smtp_conn)
{
    $data="";
    while($str = fgets($smtp_conn,515))
    {
        $data .= $str;
        if(substr($str,3,1) == " ") { break; }
    }
    return $data;
}

$header=»Date: «.date(«D, j M Y G:i:s»).» +0700\r\n»;
$header.=»From: =?windows-1251?Q?».str_replace(«+»,»_»,str_replace(«%»,»=»,urlencode(‘Максим’))).»?= <login@mail.ru>\r\n»;
$header.=»X-Mailer: The Bat! (v3.99.3) Professional\r\n»;
$header.=»Reply-To: =?windows-1251?Q?».str_replace(«+»,»_»,str_replace(«%»,»=»,urlencode(‘Максим’))).»?= <login@mail.ru>\r\n»;
$header.=»X-Priority: 3 (Normal)\r\n»;
$header.=»Message-ID: <172562218.».date(«YmjHis»).»@mail.ru>\r\n»;
$header.=»To: =?windows-1251?Q?».str_replace(«+»,»_»,str_replace(«%»,»=»,urlencode(‘Сергей’))).»?= <qwe@asd.ru>\r\n»;
$header.=»Subject: =?windows-1251?Q?».str_replace(«+»,»_»,str_replace(«%»,»=»,urlencode(‘проверка’))).»?=\r\n»;
$header.=»MIME-Version: 1.0\r\n»;
$header.=»Content-Type: text/plain; charset=windows-1251\r\n»;
$header.=»Content-Transfer-Encoding: 8bit\r\n»;

$text=»привет, проверка связи.»;

$smtp_conn = fsockopen(«smtp.mail.ru», 25,$errno, $errstr, 10);
if(!$smtp_conn) {print «соединение с серверов не прошло»; fclose($smtp_conn); exit;}
$data = get_data($smtp_conn);
fputs($smtp_conn,»EHLO vasya\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 250) {print «ошибка приветсвия EHLO»; fclose($smtp_conn); exit;}
fputs($smtp_conn,»AUTH LOGIN\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 334) {print «сервер не разрешил начать авторизацию»; fclose($smtp_conn); exit;}

fputs($smtp_conn,base64_encode(«login»).»\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 334) {print «ошибка доступа к такому юзеру»; fclose($smtp_conn); exit;}

fputs($smtp_conn,base64_encode(«password»).»\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 235) {print «не правильный пароль»; fclose($smtp_conn); exit;}

$size_msg=strlen($header.»\r\n».$text);

fputs($smtp_conn,»MAIL FROM:<login@mail.ru> SIZE=».$size_msg.»\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 250) {print «сервер отказал в команде MAIL FROM»; fclose($smtp_conn); exit;}

fputs($smtp_conn,»RCPT TO:<qwe@asd.ru>\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 250 AND $code != 251) {print «Сервер не принял команду RCPT TO»; fclose($smtp_conn); exit;}

fputs($smtp_conn,»DATA\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 354) {print «сервер не принял DATA»; fclose($smtp_conn); exit;}

fputs($smtp_conn,$header.»\r\n».$text.»\r\n.\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 250) {print «ошибка отправки письма»; fclose($smtp_conn); exit;}

fputs($smtp_conn,»QUIT\r\n»);
fclose($smtp_conn);
?>

 

Отправка письма с вложениями

<?
function get_data($smtp_conn)
{
    $data="";
    while($str = fgets($smtp_conn,515))
    {
        $data .= $str;
        if(substr($str,3,1) == " ") { break; }
    }
    return $data;
}

$header=»Date: «.date(«D, j M Y G:i:s»).» +0700\r\n»;
$header.=»From: =?windows-1251?Q?».str_replace(«+»,»_»,str_replace(«%»,»=»,urlencode(‘Максим’))).»?= <login@mail.ru>\r\n»;
$header.=»X-Mailer: The Bat! (v3.99.3) Professional\r\n»;
$header.=»Reply-To: =?windows-1251?Q?».str_replace(«+»,»_»,str_replace(«%»,»=»,urlencode(‘Максим’))).»?= <login@mail.ru>\r\n»;
$header.=»X-Priority: 3 (Normal)\r\n»;
$header.=»Message-ID: <172562218.».date(«YmjHis»).»@mail.ru>\r\n»;
$header.=»To: =?windows-1251?Q?».str_replace(«+»,»_»,str_replace(«%»,»=»,urlencode(‘Сергей’))).»?= <qwe@asd.ru>\r\n»;
$header.=»Subject: =?windows-1251?Q?».str_replace(«+»,»_»,str_replace(«%»,»=»,urlencode(‘проверка’))).»?=\r\n»;
$header.=»MIME-Version: 1.0\r\n»;
$header.=»Content-Type: multipart/mixed; boundary=\»———-A4D921C2D10D7DB\»\r\n»;

$file=»path/1.jpg»;
$fp = fopen($file, «rb»);
$code_file1 = chunk_split(base64_encode(fread($fp, filesize($file))));
fclose($fp);
$code_file2=base64_encode(«привет, это типа второй файл»);

$text=»————A4D921C2D10D7DB
Content-Type: text/plain; charset=windows-1251
Content-Transfer-Encoding: 8bit

привет, это текст письма

————A4D921C2D10D7DB
Content-Type: application/octet-stream; name=\»1.jpg\»
Content-transfer-encoding: base64
Content-Disposition: attachment; filename=\»1.jpg\»

«.$code_file1.»
————A4D921C2D10D7DB
Content-Type: application/octet-stream; name=\»2.txt\»
Content-transfer-encoding: base64
Content-Disposition: attachment; filename=\»2.txt\»

«.$code_file2.»
————A4D921C2D10D7DB—
«;

$smtp_conn = fsockopen(«smtp.mail.ru», 25,$errno, $errstr, 10);
if(!$smtp_conn) {print «соединение с серверов не прошло»; fclose($smtp_conn); exit;}
$data = get_data($smtp_conn);
fputs($smtp_conn,»EHLO vasya\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 250) {print «ошибка приветсвия EHLO»; fclose($smtp_conn); exit;}
fputs($smtp_conn,»AUTH LOGIN\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 334) {print «сервер не разрешил начать авторизацию»; fclose($smtp_conn); exit;}

fputs($smtp_conn,base64_encode(«login»).»\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 334) {print «ошибка доступа к такому юзеру»; fclose($smtp_conn); exit;}

fputs($smtp_conn,base64_encode(«password»).»\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 235) {print «не правильный пароль»; fclose($smtp_conn); exit;}

$size_msg=strlen($header.»\r\n».$text);

fputs($smtp_conn,»MAIL FROM:<login@mail.ru> SIZE=».$size_msg.»\r\n»);

$code = substr(get_data($smtp_conn),0,3);
if($code != 250) {print «сервер отказал в команде MAIL FROM»; fclose($smtp_conn); exit;}

fputs($smtp_conn,»RCPT TO:<qwe@asd.ru>\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 250 AND $code != 251) {print «Сервер не принял команду RCPT TO»; fclose($smtp_conn); exit;}

fputs($smtp_conn,»DATA\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 354) {print «сервер не принял DATA»; fclose($smtp_conn); exit;}

fputs($smtp_conn,$header.»\r\n».$text.»\r\n.\r\n»);
$code = substr(get_data($smtp_conn),0,3);
if($code != 250) {print «ошибка отправки письма»; fclose($smtp_conn); exit;}

fputs($smtp_conn,»QUIT\r\n»);
fclose($smtp_conn);
?>

 

Назад на главную