Основы PHP
  Что такое PHP?
  Возможности PHP
  Преимущества PHP
  История развития
  Что нового в PHP5?
  «Движок» PHP
  Переход на PHP 5.3
New Переход на PHP 5.6
  Введение в PHP
  Изучение PHP
  Основы CGI
  Синтаксис PHP
  Типы данных PHP
  Переменные в PHP
  Константы PHP
  Выражения PHP
  Операторы PHP
  Конструкции PHP
  Ссылки в PHP
  PHP и ООП
  Безопасность
  Функции PHP
  Функции по категориям
  Функции по алфавиту
  Стандартные функции
  Пользовательские
  PHP и HTTP
  Работа с формами
  PHP и Upload
  PHP и Cookies
  PHP и базы данных
  PHP и MySQL
  Документация MySQL
  Учебники
  Учебники по PHP
  Учебники по MySQL
  Другие учебники
  Уроки PHP
  Введение
  Самые основы
  Управление
  Функции
  Документация
  Математика
  Файлы
  Основы SQL
  Дата и время
  CURL
  Изображения
  Стили
  Безопасность
  Установка
  Проектирование БД
  Регулярные выражения
  Подготовка к работе
  Быстрый старт
  Установка PHP
  Установка MySQL
  Конфигурация PHP
  Download / Скачать
  Скачать Apache
  Скачать PHP
  Скачать PECL
  Скачать PEAR
  Скачать MySQL
  Редакторы PHP
  Полезные утилиты
  Документация
  PHP скрипты
  Скачать скрипты
  Инструменты
  PHP в примерах
  Новости портала
 Главная   »  Сборник статей
 
 

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


Источник: http://webi.ru

В этой статье я расскажу, как отправлять почту через 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 код и запустите его на тестовом сервере.

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

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

235 Authentication succeeded

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

MAIL FROM:login@mail.ru

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

250 OK

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

RCPT TO:asd@qwe.ru

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

250 Accepted

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

DATA

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

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

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

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

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

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

250 OK id=1IiR72-000ONs-00

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

QUIT

ответ

221 mx30.mail.ru closing connection

Вот такой принцип общения с SMTP сервером.
Здесь я показал только принцип.
Полное описание протокола smtp, все команды и коды ответов сервера читайте в документе PDF на русском языке протокол SMTP


Отправка через php.

А сейчас все это общение с сервером переведем на 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.
Это письмо не будет отличаться от отправленного через этот почтовик.

<?php
$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);

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

<?php
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:login@mail.ru\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.
Подготовим две текстовые переменные, содержащие файлы в кодированном виде.

<?php
$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, которая у нас является телом письма.

<?php
$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" я создал случайным образом, он может быть любой по вашему усмотрению, главное, он должен начинаться с "--" и не встречаться в самом письме...


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

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

<?php
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 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:login@mail.ru\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);
?>


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

<?php
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 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:login@mail.ru\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 = <;/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);
?>

 

 
 » Обсудить эту статью на форуме

 
 Сборник статей 
 Содержание раздела 
Есть еще вопросы или что-то непонятно - добро пожаловать на наш  форум портала PHP.SU 
 

 
Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS