From: Денис Назаров <marsden at mail.ru>
Newsgroups: email
Date: Mon, 30 Aug 2008 18:21:07 +0000 (UTC)
Subject: Отправка писем с авторизацией на SMTP-сервере из Perl
Введение
Возникла задача - отправка письма без использования sendmail, без настройки
своего smtp-сервера - то есть на минимальной десктопной системе. Осложнялась
задача тем, что к письму необходимо было прицеплять вложения. Отсутсвие
своего почтового сервера привело к решению использовать ящик на одном
из бесплатных почтовых серверов, в частности - mail.ru. Готовых примеров
в интернете нашлось немало, но почти у всех были недостатки - использование
sendmail или других сторонних программ и самый главный - отсутствие авторизации
на smtp сервере. Нашелся, впрочем, в котором довольно подробно описан процесс
взаимодействия с сервером, в том числе авторизация. В результате получился
небольшой скрипт на perl, который можно легко доработать под свои нужды.
Результат
#!/usr/bin/perl
use MIME::Base64; #для кодирования авторизационных параметров, темы и тела письма
use IO::Socket; #для общения с SMTP-сервером
use Text::Iconv; #для перекодирования текста
my $mailbox = 'xxxxxx@mail.ru'; # ящик-отправитель
my $mailpwd = 'xXxYyYzZz'; # пароль
my $mailrcpt = 'yyyyyyy@mail.ru'; # ящик-получатель
my $subj = 'Привет, это я!';
my $mail = 'Отправляю тебе свою фотку с Черного моря :)';
my $attachment = 'foto.jpg'; # файл-вложение
my $attachpath = '/home/user/Documents/'; # путь к нему
# поскольку данный скрипт я пишу в кодировке UTF-8,
# а письмо хочу отправлять в кодировке Windows, то мне просто необходим
# перекодировщик для кодирования из UTF-8(юникод) в CP1251(Windows)
my $cnv = Text::Iconv->new('UTF8','CP1251');
my $reply; # код ответа сервера
my $message; # текст ответа сервера
#открываем сокет к SMTP-серверу
my $socket = IO::Socket::INET->new('smtp.mail.ru:25');
defined $socket or die "ERROR: $!n";
# читаем ответ
if(ReadReply() ne 220){print "Ошибка установки связи = $messagen"; $socket->close(); exit}
# здороваемся с сервером - не стал использовать ehlo,
# потому что расширенные возможности не требуются
$socket->print ("helo lon");
# получаем ответ и проверяем код
if(ReadReply() != 250){print "Ошибка приветствия сервера = $messagen"; $socket->close(); exit}
# теперь проводим авторизацию
$socket->print("AUTH LOGINn");
# получаем ответ
if(ReadReply() ne 334){print "Ошибка авторизации = $messagen"; $socket->close(); exit}
# кодируем логин-пароль
$socket->print(encode_base64($mailbox).encode_base64($mailpwd));
# после авторизации выдается две строчки
ReadReply();
if(ReadReply() ne 235){print "Ошибка авторизации = $messagen"; $socket->close(); exit}
# начинаем транзакцию - даем команду отправки письма
$socket->print('mail from: '."$mailboxn");
if(ReadReply() ne 250){print "Ошибка в почтовом ящике отправителя = $messagen"; $socket->close(); exit}
# указываем получателя
$socket->print("rcpt to: $mailrcptn");
if(ReadReply() ne 250){print "Ошибка в почтовом ящике получателя = $messagen"; $socket->close(); exit}
# теперь начинаем формировать письмо
$socket->print("datan");
if(ReadReply() ne 354){print "Ошибка при начале формирования письма = $messagen"; $socket->close(); exit}
# теперь сформируем тему письма, перекодировав ее из юникода в ср-1251
# и потом закодировав все это в base64.
# Таким образом, в теме письма можно нормально писать по русски
# Если вы используете другую кодировку в системе и/или при подготовке
# этого скрипта под свои нужды - скорректируйте создание конвертера в начале
# или вообще откажитесь от него
$subj = encode_base64($cnv->convert($subj));
$subj =~ s/n//ig; # уберем символы перевода строки
$subj =~ s/r//ig; # и возврата каретки, поскольку они все ломают :)
$subj = '=?Windows-1251?B?'.$subj.'?=';
# создадим тело письма
$msg = encode_base64($cnv->convert($mail));
# здесь формируем заголовок, минимальная версия
$body = "Mime-Version: 1.0n";
$body .= "Content-Type: multipart/mixed; boundary="-"nn";
# вставляем тело письма
$body .= "---nContent-Type: text/plain;ntcharset="Windows-1251"nContent-Transfer-Encoding: base64nn$msgn";
# скинем письмо серверу
$socket->print($mailmessage);
# и посмотрим что получилось
if(ReadReply() ne 250){print "Ошибка при отправке письма = $messagen"; $socket->close(); exit}
# если дошли до этого места, значит письмо ушло
$socket->close();
print "Письмо отправленоn";
sub ReadReply{
# процедура чтения ответа от сервера
# цикл используется для того, чтобы прочитать многострочный ответ
# например, при выдаче ответа на команду EHLO
# формат строк
# Трехзначное число-пробел или дефис-текстовое сообщение
# причем, если ответ многострочный, то дефис используется во всех
# строках, кроме последней, в которой используется пробел
# именно по этому признаку и будет определятся конец цикла
# и возвращаемый код будет браться также из последней строки
$val = 1;
while($val eq 1){
$r = <$socket>;
$val = $r =~ m/^d{3}-/g;
}
($reply,$message) = split(/ /,$r,2);
return $reply;
}
Ссылки
пример на php - http://webi.ru/webi_articles/6_10_f.html
пример использования сокетов - http://www.opennet.ru/base/dev/perl_sendmail.txt.html
Денис Назаров
marsden at mail.ru
август 2008
1065 Прочтений • [Отправка писем с авторизацией на SMTP-сервере из Perl (smtp auth perl)] [08.05.2012] [Комментариев: 0]