Pull to refresh

Comments 13

Комментируем! Создал «первый коммент», чтоб можно было комментить, пока Хабру не починят :) Подробности: habrahabr.ru/qa/12275/
«Беда, коль пироги начнет печи сапожник...» — неужели рядом не было ни одного сисадмина?
А вообще — уважаю за дотошность и умение справляться имеющимися средствами!

Я бы, конечно, просто написал скрипт периодически забирающий из базы данные, вставляющий их в шаблон и производящий рассылку.
Очень, ну очень хочется вставить известную картинку про троллейбус.
У меня тоже была первая мысль, что писма слать — задача не для СУБД.
По моему, эта статья — пример того, как делать не надо:

1) использовать СУБД для рассылки почты
2) ставить на сервер утилиты, не понимая принципа их работы. Как понимаю, ваш SMTP-релей открыл порт не на loopback-интерефйсе, а на всех интерфейсах, включая внешний, и его нашли спамеры. Вы бы еще гостевой доступ на Windows Server разрешили (или пароль root поменяли на 123456, чтобы набирать проще было).
3) ну и вообще, использовать сложные технологии, не понимая даже что такое TLS, зачем открывают порт 2525 и т.д.

В общем, имхо, крайне плохая статья. Если вам надо было что-то разослать на основе данных в базе, а желания читать мануалы и википедии нет, надо было просто экспортировать базу в нужном виде в Excel/Access и через outlook разослать — у майкрософта что-то есть для рассылки писем из таблицы, и особых знаний там не требуется, только кнопки «Далее» и «ОК» нажимать.

Хранить пароли и логины в открытом виде, тоже, кстати, не лучшая идея.
А ведь и без запарок можно с помощью cron'a, sql*plus'a и любого консольного почтового клиента
На счет отсылки писем из СУБД — не вижу в этом ничего предосудительного (когда, конечно, все происходит в нормально сконфигурированной среде). Просто существует два диаметрально противоположных подхода: БД — только хранилище данных и БД — серверная часть приложения, как правило в живых проектах используется что-то среднее с перекосом в ту или другую сторону.
Если в ваших проектах привыкли не развертывать логику в БД, то да, отсылка оттуда писем, http-запросов и прочий не sql-функционал выглядит неуместно. Если наличие в БД логики для вас норма, все данные находятся и обрабатываются там же, почему бы не отослать письмо с отчетом? Тем более, что это выполняется стандартными средствами, без вызова «external subprograms».

На счет замечаний по моей настройке почты — то все замечания верные, и, в идеальном мире, я бы этим не занимался. Но в реальности бывают разные обстоятельства, разные люди, по-разному выполняющие свою работу. В общем, не займись я этим сам, слал бы я письма руками.

На счет паролей в открытом виде, то в данном случае пароли — не очень критичная информация, в том смысле, что это автоматически сгенерированные пароли для доступа к определенной функциональности в БД (потому их и нужно было рассылать). Хранятся они вместе с той функциональностью, доступ к которой обеспечивают. Другими словами, атака, способная достигнуть таблицы с паролями, равновероятно достигнет и защищаемой ими информации. Поэтому с точки зрения возможности кражи шифровать их бессмысленно, разве что для защиты от передачи по сети в открытом виде. Но тут уже встает вопрос соотношения цены информации к цене ее защиты. С точки зрения критичности такой перехват пароля позволит злоумышленнику всего лишь получить доступ к системе работы с данными, заточенной под весьма специфические операции, а не к самим данным.

Про автоматизированную отсылку через почтовый клиент типа Outlook'a даже не подумал — давно не пользовался, вот специфика мышления и определила однозначное направление поиска решения :)
>>На счет отсылки писем из СУБД — не вижу в этом ничего предосудительного
Поддерживаю. Мои ночные жобы часто шлют мыло о сбоях мне и на вторую линию поддержки. В интрасеть, правда, не во вне.
>>для русского текста желательно использовать 'text/html; charset=«UTF-8»'.
Все мои базы в cp-1251, шлю русский текст преобразуя его в raw:

  UTL_SMTP.write_raw_data(l_connection
                          ,UTL_RAW.cast_to_raw(
                                     'Content-Type: text/plain; charset=windows-1251'||crlf
                                     ||'Subject:=?windows-1251?Q?'||subject||'?='||crlf
                                     ||crlf||message||crlf
                            )
                         );
Тоже поддержу, у меня при инсерте в таблицу блокировок заявок рассылаются емейлы пользователям, о том что им неплохо бы подойти к начальству )
К сожалению похожая ситуация не только с Oracle, но и с почтовыми серверами в целом.
Для меня как человеку далекому от админства настраивать такое превращается в головную боль на день или два.
Позволю себе вставить пару центов. (Впрочем, центов — много! Копеек.)

Полгаю, что в статье вы рассказали об эксплуатации такого софта в условиях ограниченных ресурсов. По-просту, дома. Экспериментируя дома, наверно, можно позволить себе объединить на одной машине все экспериментаторские сервисы и совершать собственные ошибки в настройках.

Но софт такого уровня обычно эксплуатируется в крупных организациях. Где уже есть инфраструктура.

Надо понимать, что задача отправки почты «средствами Oracle» разбивается на две (или даже больше):
— сформировать и отправить сообщение на стандартный smtp сервер
— иметь в наличии этот самый smtp сервер

Про то, как сформировать и отправить письмо вы хорошо написали. Своим «программистам» я рекомедую делать так:

DECLARE

vSender VARCHAR2(30) := 'oracle@name-of-db-goes-here.domain.name.here';
vSubj VARCHAR2(50) := 'Hello';
vRec VARCHAR2(50) := 'receiver@domain.name.here';
vMesg VARCHAR2(4000);
vMType VARCHAR2(40) := 'text/plain; charset=ISO-8859-5';

BEGIN

select TO_CHAR(sysdate,'HH24:MI:SS DD.MM.YYYY') into vMesg from dual;

vMesg := vMesg || CHR(13) || CHR(10) ||
'Hello world '|| CHR(13) || CHR(10) ||
'I am so happy!' || CHR(13) || CHR(10) ||
'И могу говорить по-русски!';

utl_mail.send(vSender, vRec, NULL, NULL, vSubj,vMesg, vMType, NULL);

END;

*Выбор кодировки (charset=ISO-8859-5) связан с особенностями работы наших Oracle под Solaris.

Теперь о второй задаче «иметь в наличии smtp сервер». Решается она очень просто: используйте свой корпоративный почтовый сервер! :) Или сервер заведомо работающего провайдера, что, собственно, вы и сделали.

(Или сделайте собственный почтовый сервер! Что, кстати, не так сложно, как кажется. Или наймите того, кто это может. Меня, например. :))

Или, все-таки, используйте «SMTP прокси», но закройте его файрволлом от подключения «извне».

Полностью согласен на счет корпоративного почтового сервера, насколько это большое благо я понял только когда пришлось слать почту без него (что и описано выше). «В условиях ограниченных ресурсов» — замечательная формулировка полностью передающая суть описанного в статье, хотя это не домашняя машина.

Раз уж такое дело, то добавлю пару слов о использовании внешних почтовых серверов. Скажем прямо, дело это мутное, в том смысле, что существуют разные ньансы.

Свой код для отправки писем с требующего авторизации почтового сервера привожу ниже. Скажу сразу, что этот код может слать письма в русской кодировке, например на Gmail. Успешность отсылки зависит от принимающего ящика, т.е. если ваше русскоязычное письмо пришло в неподобающем виде — попробуйте поменять строки с кодировкой.
Сам код:
create or replace procedure send_mail_with_authorization(p_mail_host varchar2, p_mail_port pls_integer:=587,
                                                         p_sender varchar2, p_password varchar2,
                                                         p_recipients varchar2, p_subject varchar2, 
                                                         p_message varchar2) is
  v_mail_conn utl_smtp.connection;
  raw_message	raw(32000);
  raw_subject raw(32000);
  
begin
  v_mail_conn := utl_smtp.open_connection(p_mail_host, p_mail_port);
  utl_smtp.ehlo(v_mail_conn, p_mail_host);
  utl_smtp.command(v_mail_conn, 'AUTH LOGIN');
  utl_smtp.command(v_mail_conn, utl_raw.cast_to_varchar2(utl_encode.base64_encode(
utl_raw.cast_to_raw(p_sender))));
  utl_smtp.command(v_mail_conn, utl_raw.cast_to_varchar2(utl_encode.base64_encode(
utl_raw.cast_to_raw(p_password))));
  utl_smtp.mail(v_mail_conn, p_sender);
  utl_smtp.rcpt(v_mail_conn, p_recipients);
  utl_smtp.open_data(v_mail_conn);
  utl_smtp.write_data(v_mail_conn, 'Date: ' || to_char(sysdate, 'dd-mm-yyyy hh24:mi:ss') || utl_tcp.CRLF);
  utl_smtp.write_data(v_mail_conn, 'From: ' || p_sender || utl_tcp.CRLF);
  utl_smtp.write_data(v_mail_conn, 'To: ' || p_recipients || utl_tcp.CRLF);
  
--  utl_smtp.write_raw_data(v_mail_conn, utl_raw.cast_to_raw('Content-Transfer-Encoding: base64'||
UTL_TCP.CRLF));
  utl_smtp.write_data(v_mail_conn,'Content-Type: text/html; charset=UTF-8'||UTL_TCP.CRLF);
  raw_subject := UTL_RAW.cast_to_raw(convert(p_subject,'AL32UTF8'));
  raw_message:=utl_raw.cast_to_raw(convert(p_message,'AL32UTF8'));


  utl_smtp.write_data(v_mail_conn, 'Subject: ');
  utl_smtp.write_raw_data(v_mail_conn, raw_subject);
  utl_smtp.write_data(v_mail_conn, utl_tcp.CRLF);
  utl_smtp.write_raw_data(v_mail_conn, raw_message);
  utl_smtp.write_data(v_mail_conn, utl_tcp.CRLF);  
  
  utl_smtp.close_data(v_mail_conn);
  utl_smtp.quit(v_mail_conn);
end;


Этот код у меня успешно слал письма с Рамблера и Yahoo. Вызов происходит так:
begin
  send_mail_with_authorization(p_mail_host => 'mail.rambler.ru',
                               p_mail_port => 587,
                               p_sender => 'my_mail@rambler.ru',
                               p_password => 'my_pass',
                               p_recipients => 'my_friend@gmail.com',
                               p_subject => 'Тема',
                               p_message => 'Текст письма.');

  send_mail_with_authorization(p_mail_host => 'smtp.mail.yahoo.com',
                               p_mail_port => 587,
                               p_sender => 'my_mail@yahoo.com',
                               p_password => 'my_pass',
                               p_recipients => 'my_friend@gmail.com',
                               p_subject => 'Тема',
                               p_message => 'Текст письма.');
end;


Теперь о ньюансах: их масса, и всех я не знаю, но то с чем столкнулся расскажу:
1. Иногда почта с Yahoo некорректно отображается на Gmail, т.е. я слал одно письмо с Yahoo на 3 разных почтовика, везде все ок, а на Gmail письмо приходит с пустым телом. Почему так — не знаю, но если то же самое письмо отправить с Рамблера, то Gmail его видит отлично.
2. Русский текст. Тут также «темна вода в облацеях» — вы може подобрать параметры, которые будут распознаваться большинством серверов, но на каком-то вы все-равно получите кроказябры. Таким образом идеал — когда вам нужно слать письма только пользователям одного почтового сервера, чтоб вы кастомизировали под него кодировку и тип сообщения (plain/html).
3. Рамблер странный, может мне просто не повезло с временем экспериментов, но суть такова: у меня есть ящик на Рамблере, я протестил указанную выше процедуру — через него она почту шлет отлично. Специально для нужн системы завел новый ящик, и через него почта не пошла: выпадают поочередно ошибки вида «Incorrect username/password» или «Internal server error». Работа с настройками ящиков ни к чему не привела — они одинаковы, возможно, на сервере стоит какой-то таймаут для использования smtp для новых ящиков, хотя звучит это как-то диковато.
Sign up to leave a comment.

Articles

Change theme settings