Отправка писем через SMTP с авторизацией


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

Для чего же может потребоваться отправка почты из php через smtp, ведь в php есть готовая функция отправки почты mail()?

1. Основная причина, это проблема фильтров антиспама.
Современные сервера настроены таким образом, что отправляя письмо через функцию mail(), в заголовки письма добавляется информация, по которой очевидно, что почта была отправлена с помощью mail().
Вот этого и не любят фильтры антиспама на стороне получателя. Не все конечно, но если фильтр настроен жестко, то скорее всего такое письмо будет удалено фильтром на стороне получателя.
Можно конечно избавиться от таких заголовков с помощью настройки сервера(если у вас есть доступ к настройке сервера), но это уже вариант сложный и не всем подходит.

2. Специфическая причина.
Можно полностью замаскировать письмо, как буд-то оно было отправленно почтовой программой.

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

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

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

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

Теперь здороваемся с сервером

EHLO mail.ru

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

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

Авторизация пройдена. Сейчас указываем от кого будет написано письмо. Указываете свой ящик, от имени которого авторизовались.

MAIL FROM:[email protected]

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

250 OK

Теперь указываем email получателя

RCPT TO:[email protected]

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

250 Accepted

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

DATA

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

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

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

Subject: Это тема письма
To: [email protected]
X-Mailer: webi.ru mailer

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

.

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

250 OK id=1IiR72-000ONs-00

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

QUIT

ответ

221 mx30.mail.ru closing connection

Вот такой принцип общения с 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('Максим')))."?= <[email protected]>\r\n";
$header.="X-Mailer: The Bat! (v3.99.3) Professional\r\n";
$header.="Reply-To: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Максим')))."?= <[email protected]>\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('Сергей')))."?= <[email protected]>\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 mail.ru\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);

fputs($smtp_conn,"MAIL FROM:[email protected]\r\n");
$data = get_data($smtp_conn);

fputs($smtp_conn,"RCPT TO:[email protected]\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, но и имя (Максим <[email protected]>).
Вот только при написании имен русскими символами, придется их конвертировать
=?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Максим')))."?= <[email protected]>
По такому же принципу кодируется и тема...

Еще одно интересное поле Message-ID:
Это идентификатор сообщения. Должен быть уникальным в пределах вселенной.
В примере я использовал принцип создания идентификатора почтовика The bat.
Message-ID: <172562218.".date("YmjHis")."@mail.ru>
после преобразования получится примерно так
Message-ID: <[email protected]>
Каждые почтовики создают это поле по-своему. 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('Максим')))."?= <[email protected]>\r\n";
$header.="X-Mailer: The Bat! (v3.99.3) Professional\r\n";
$header.="Reply-To: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Максим')))."?= <[email protected]>\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('Сергей')))."?= <[email protected]>\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 mail.ru\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;}

fputs($smtp_conn,"MAIL FROM:[email protected]\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:[email protected]\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('Максим')))."?= <[email protected]>\r\n";
$header.="X-Mailer: The Bat! (v3.99.3) Professional\r\n";
$header.="Reply-To: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode('Максим')))."?= <[email protected]>\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('Сергей')))."?= <[email protected]>\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 mail.ru\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;}

fputs($smtp_conn,"MAIL FROM:[email protected]\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:[email protected]\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 = <;/font>substr(get_data($smtp_conn),0,3);
if(
$code != 250) {print "ошибка отправки письма"; fclose($smtp_conn); exit;}

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

© webi.ru

Оценить Статью:  
1   2   3   4   5   6   7   8   9   10    

« Назад
SAPE все усложнил?

MainLink - простая и прибыльная продажа ссылок!

Последние поступления:

Размещена 10 августа 2020 года

Я по ТВ видел, что через 10 лет мы будем жить лучше, чем в Германии...
Я не понял, что это они с Германией сделать хотят?!

читать далее…

ТехЗадание на Землю

Размещена 14 марта 2018 года

Пpоект Genesis (из коpпоpативной пеpеписки)

читать далее…

Шпаргалка по работе с Vim

Размещена 05 декабря 2017 года

Vim довольно мощный редактор, но работа с ним не всегда наглядна.
Например если нужно отредактировать какой-то файл например при помощи crontab, без знания специфики работы с viv никак.

читать далее…

Ошибка: Error: Cannot find a valid baseurl for repo

Размещена 13 сентабря 2017 года

Если возникает ошибка на centos 5 вида
YumRepo Error: All mirror URLs are not using ftp, http[s] or file.
Eg. Invalid release/

читать далее…

Linux Optimization

Размещена 30 июля 2012 года

Prelink

читать далее…