Гость
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Идеальная рассылка почты из PL/SQL UTL_SMTP / 14 сообщений из 14, страница 1 из 1
13.02.2020, 13:37
    #39926238
Идеальная рассылка почты из PL/SQL UTL_SMTP
Братцы, да понятно, что все знают UTL_MAIL, UTL_SMTP и даже где-то UTL_TCP. У меня тоже код работает по рассылке почты от корпоративного почтаря. Рассылается статус заказов пользователям облачного приложения ну и всякое такое деловое, неспамовское по сути изначально. Всё было хорошо, пока не тукнуло в mail.ru спам-блокировкой почты домена. Юноши там немного странные, но речь не о них. В итоге я даже немного благодарен этому не совсем адекватному с моей скромной точки зрения почтарю.

Итак, поскольку во мне живёт программер, раззудилось добиться от четырёх источников gmail, ya.ru, mail.ru и mail-tester.com сердечного одобрения. От роботов конечно.

В итоге получил по текстовым и письмам с одним вложением (pdf, docx) X-Spam-Status: score=-2.9 по первым трём и чистые 10/10 для последнего.
Привожу код в надежде, что здесь полно умных людей и они толково ещё что-то скажут. Хотя я не уверен, что результат можно улучшить ))

Прицепом замечу, что APEX_MAIL так себе пакетик. А за utl_encode и base64_encode нужен глаз да глаз: то недолив переноса строк, то перелив.

1. Пока у вас нет dkim, spf и dmarc на домене, можете не мечтать особо.
2. Если есть, то код получился примерно таким.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
CREATE OR REPLACE PROCEDURE SEND_MAIL (
                                       p_to        IN VARCHAR2,
                                       p_from      IN VARCHAR2,
                                       p_from_name IN VARCHAR2,
                                       p_reply_to  IN VARCHAR2,
                                       p_subject   IN VARCHAR2,
                                       p_body      IN VARCHAR2 DEFAULT NULL,
                                       p_body_html IN VARCHAR2 DEFAULT NULL,
                                       p_attach_name IN VARCHAR2 DEFAULT NULL,
                                       p_attach_mime IN VARCHAR2 DEFAULT NULL,
                                       p_attach_blob IN BLOB DEFAULT NULL)
AS
  l_mail_conn   UTL_SMTP.connection;
  l_boundary1   varchar2(50);
  l_boundary2   varchar2(50);
  l_step        pls_integer  :=9000;
  l_smtp_host   varchar2(50) := 'Ваш почтарь';
  l_smtp_port   number := 25;
  l_attach_mime varchar2(255); 

function encodeQ(subj_str varchar2) return varchar2
AS
begin
  return replace(UTL_ENCODE.MIMEHEADER_ENCODE (subj_str, 'utf8', UTL_ENCODE.QUOTED_PRINTABLE),UTL_TCP.crlf,''); -- replace важно!
end;

function get_boundary return varchar2 is
begin
 return upper(MD5HASH(current_timestamp));
end;

BEGIN
  l_boundary1 := '--=:'||get_boundary||':';
  l_boundary2 := '--=:'||get_boundary||':';
  
  l_mail_conn := UTL_SMTP.open_connection(l_smtp_host, l_smtp_port);
  UTL_SMTP.helo(l_mail_conn, l_smtp_host);
  UTL_SMTP.mail(l_mail_conn, p_from);
  UTL_SMTP.rcpt(l_mail_conn, p_to);
  UTL_SMTP.open_data(l_mail_conn);
  
  UTL_SMTP.write_data(l_mail_conn, 'MIME-Version: 1.0' || UTL_TCP.crlf);  
  UTL_SMTP.write_data(l_mail_conn, 'To: ' || p_to || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'From: ' || encodeQ(p_from_name) || '<' || p_from ||'>' || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Reply-To: ' || nvl(p_reply_to, p_from) || UTL_TCP.crlf);
  if p_attach_name is null then
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: multipart/alternative; boundary="' || l_boundary1 || '"' || UTL_TCP.crlf);
  else
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: multipart/mixed; boundary="' || l_boundary1 || '"' || UTL_TCP.crlf);
  end if;
  UTL_SMTP.write_data(l_mail_conn, 'Content-Transfer-Encoding: 8bit'|| UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Date: ' || to_char(current_timestamp,'Dy, DD Mon YYYY HH24:MI:SS TZHTZM', 'NLS_DATE_LANGUAGE=AMERICAN') || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Subject: ' || encodeQ(nvl(p_subject,'Тема письма не указана.')) || UTL_TCP.crlf);

  -- для почтарей комментируем этот кусок, а для mail-tester оставляем
  UTL_SMTP.write_data(l_mail_conn, 'List-Unsubscribe: <mailto:unsubscribe@вашдомен.com?subject=Unsubscribe>'|| UTL_TCP.crlf ); 
  UTL_SMTP.write_data(l_mail_conn, 'List-Subscribe: <mailto:subscribe@вашдомен.com?subject=Subscribe>'|| UTL_TCP.crlf );
  --------------------------------------------------------------------
  
  UTL_SMTP.write_data(l_mail_conn, 'List-Owner: <mailto:abuse@вашдомен.com?subject=Abuse>'|| UTL_TCP.crlf );
  UTL_SMTP.write_data(l_mail_conn, 'List-Help: <mailto:help@вашдомен.com?subject=Help>'|| UTL_TCP.crlf || UTL_TCP.crlf);
  
  UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary1 || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Content-Type: multipart/alternative; boundary="' || l_boundary2 || '"' || UTL_TCP.crlf || UTL_TCP.crlf);
   
  if p_body is not null then
    UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary2 || UTL_TCP.crlf);
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: text/plain; charset="utf-8"' || UTL_TCP.crlf || UTL_TCP.crlf);
    UTL_SMTP.write_raw_data(l_mail_conn, UTL_RAW.cast_to_raw(p_body)); -- write_raw_data важно
    UTL_SMTP.write_data(l_mail_conn, UTL_TCP.crlf);
  end if;

  if p_body_html is not null then
    UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary2 || UTL_TCP.crlf);
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: text/html; charset="utf-8"' || UTL_TCP.crlf || UTL_TCP.crlf);
    UTL_SMTP.write_raw_data(l_mail_conn, UTL_RAW.cast_to_raw(p_body_html)); -- write_raw_data важно
    UTL_SMTP.write_data(l_mail_conn, UTL_TCP.crlf );
  end if;
  
  UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary2 || '--' || UTL_TCP.crlf || UTL_TCP.crlf);
  
  if p_attach_name is not null then
    UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary1 || UTL_TCP.crlf);
    
    if instr(p_attach_name,'.pdf')>0 then l_attach_mime := 'application/pdf';  
    elsif instr(p_attach_name,'.docx')>0 then l_attach_mime := 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
    else l_attach_mime := p_attach_mime;
    end if;
    
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: ' || l_attach_mime || '; name="' || p_attach_name || '"' || UTL_TCP.crlf);
    UTL_SMTP.write_data(l_mail_conn, 'Content-Disposition: attachment; filename="' || p_attach_name || '"' || UTL_TCP.crlf);
    UTL_SMTP.write_data(l_mail_conn, 'Content-Transfer-Encoding: base64' || UTL_TCP.crlf || UTL_TCP.crlf);

    for i in 0 .. trunc((DBMS_LOB.getlength(p_attach_blob) - 1 )/l_step) 
    loop
      UTL_SMTP.write_data(l_mail_conn, UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(DBMS_LOB.substr(p_attach_blob, l_step, i * l_step + 1))) || UTL_TCP.crlf); -- || UTL_TCP.crlf важно 
    end loop; 

    UTL_SMTP.write_data(l_mail_conn, UTL_TCP.crlf || UTL_TCP.crlf);
  end if;

  UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary1 || '--' || UTL_TCP.crlf);
  UTL_SMTP.close_data(l_mail_conn);

  UTL_SMTP.quit(l_mail_conn);
  
  exception 
    when others then
            UTL_SMTP.close_data(l_mail_conn);
            UTL_SMTP.quit(l_mail_conn);          
END;



PS Что касается mail.ru, то юноши эти могут запросто отреагировать на пару тычков в спам некоего вредного (или злонамеренного) пользователя и занести весь ваш домен в спам-лист. Также опция "Отписаться" напрочь игнорирует у них RFC, а просто направляет вашего отправителя в спам.
...
Рейтинг: 0 / 0
13.02.2020, 13:43
    #39926243
Идеальная рассылка почты из PL/SQL UTL_SMTP
А да, виноват, вот код хэша, не помню, откуда взял, но работает )))
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
CREATE OR REPLACE FUNCTION MD5HASH (str IN VARCHAR2)
	RETURN VARCHAR2
	IS v_checksum VARCHAR2(32);
 
	BEGIN
		if str is null then 
      v_checksum:='';
    else 
      v_checksum := lower( rawtohex( utl_raw.cast_to_raw( sys.dbms_obfuscation_toolkit.md5(input_string => str) ) ) );
    end if;
    
		RETURN v_checksum;
		EXCEPTION
			WHEN NO_DATA_FOUND THEN
      return '';
			NULL;
      
		WHEN OTHERS THEN
			-- Consider logging the error and then re-raise
      return '';
			RAISE;
	END md5hash;
...
Рейтинг: 0 / 0
13.02.2020, 13:53
    #39926251
-2-
-2-
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Идеальная рассылка почты из PL/SQL UTL_SMTP
Саша Сплинтер
что-то скажут
Слишком много говна, чтобы раскладывать его по полочкам.
...
Рейтинг: 0 / 0
13.02.2020, 13:55
    #39926252
Идеальная рассылка почты из PL/SQL UTL_SMTP
-2-,

Давайте свой код сюда, который решает поставленную задачу. У меня никаких претензий.
С удовольствием буду пользоваться.
...
Рейтинг: 0 / 0
13.02.2020, 14:05
    #39926260
123йй
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Идеальная рассылка почты из PL/SQL UTL_SMTP
Саша Сплинтер,

здесь, на форуме, был более интересный пакет
...
Рейтинг: 0 / 0
13.02.2020, 14:16
    #39926265
Идеальная рассылка почты из PL/SQL UTL_SMTP
Да здесь на форуме вообще полно всякого. Но я не нашёл.
Просто мой код задачу решает какую я отписал. А сферически рассылать умеет полинтернета.
...
Рейтинг: 0 / 0
13.02.2020, 14:23
    #39926268
123йй
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Идеальная рассылка почты из PL/SQL UTL_SMTP
Саша Сплинтер,

учись .
Тогда свою подделку не будешь называть "Идеальная рассылка"
...
Рейтинг: 0 / 0
13.02.2020, 14:54
    #39926282
Идеальная рассылка почты из PL/SQL UTL_SMTP
Вообще супер!
И чего там только нету! В этой тонне кода!
Пару слов в защиту моей поделки (одна "д").

Моя поделка маленькая и решает, как я написал, вполне конкретную задачу 10 из 10. что я и назвал идеальной рассылкой.

У автора в его шикарном пакете задачи другие. И если прогнать по описанным критериям рассылку с помощью его пакета, то результаты хорошие, но не отличные.

Так что пишите ещё )
...
Рейтинг: 0 / 0
13.02.2020, 15:10
    #39926288
Идеальная рассылка почты из PL/SQL UTL_SMTP
Добавлю, что с этого у меня все и начиналось, когда вопрос встал не о мега-пакете (да мне такого и не написать), а о результатах тестов текста сообщения:


-0.001 BAD_ENC_HEADER Message has bad MIME encoding in the header
-2.019 BASE64_LENGTH_79_INF base64 encoded email part uses line length greater than 79 characters
-1.105 MIME_HTML_ONLY Message only has text/html MIME parts

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

За ссылку на пакет благодарю.
...
Рейтинг: 0 / 0
13.02.2020, 15:16
    #39926290
Elic
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Идеальная рассылка почты из PL/SQL UTL_SMTP
Саша Сплинтер
расскажите, что есть идеальный код, а что есть решающий задачу код.
Чего не навыдумывают, чтобы хвастаться свои говнокодом.
...
Рейтинг: 0 / 0
13.02.2020, 15:23
    #39926293
Идеальная рассылка почты из PL/SQL UTL_SMTP
Вот ещё один достойный комментарий!
Итак, а вы что предложите, чей чужой пакет проверить?
Ну или может снизойдёте и укажете на причины Вашей столь нелестной оценки?
В самом деле буду благодарен.
Кушать не смогу теперь! Вам до 30 000 сообщений всего ничего осталось! Дерзайте!
...
Рейтинг: 0 / 0
13.02.2020, 15:27
    #39926295
Идеальная рассылка почты из PL/SQL UTL_SMTP
автор-2.019 BASE64_LENGTH_79_INF base64 encoded email part uses line length greater than 79 characters

Вот эта ерунда получается вот здесь

UTL_SMTP.write_data(l_mail_conn, UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(DBMS_LOB.substr(p_attach_blob, l_step, i * l_step + 1))) || UTL_TCP.crlf);

Если не добавлять || UTL_TCP.crlf к UTL_RAW.cast_to_varchar2, то, поскольку UTL_ENCODE.base64_encode не оканчивает блок crlf, происходит объединение двух строк в одну.

В мегапакете, автор которого в этой дискуссии отсутствует, этот кусок выглядит так

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
   LOOP
      BEGIN
        dbms_lob.read(message, v_amt, ps, vBuf);
        ps := ps + v_amt;
        utl_smtp.write_raw_data (v_Mail_Conn,utl_encode.base64_encode ( utl_raw.cast_to_raw(convert(vBuf,'UTF8'))));
      EXCEPTION
        WHEN no_data_found THEN
            EXIT;
      END;
    END LOOP;
...
Рейтинг: 0 / 0
13.02.2020, 19:18
    #39926404
merch
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Идеальная рассылка почты из PL/SQL UTL_SMTP
Саша СплинтерПросто мой код задачу решает какую я отписал
Саша СплинтерМоя поделка маленькая и решает, как я написал, вполне конкретную задачу 10 из 10
Саша СплинтерТак что всё честно я написал про идеальную рассылку

а что Вы от форумчан хотите? Восхищения? Или чтобы мы сели и написали код, решающий вашу задачу на 11 из 10?
...
Рейтинг: 0 / 0
13.02.2020, 20:21
    #39926436
SQL*Plus
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Идеальная рассылка почты из PL/SQL UTL_SMTP
merch
Саша СплинтерПросто мой код задачу решает какую я отписал

Саша СплинтерМоя поделка маленькая и решает, как я написал, вполне конкретную задачу 10 из 10
Саша СплинтерТак что всё честно я написал про идеальную рассылку

а что Вы от форумчан хотите? Восхищения? Или чтобы мы сели и написали код, решающий вашу задачу на 11 из 10?
Саша Сплинтер!

Несите сюда пятилитровую бутылку хорошего 12 летнего виски!
Будем обмывать успех.

Никто не возражает?

P.S. splinter = щепка, осколок, заноза
:-)
...
Рейтинг: 0 / 0
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Идеальная рассылка почты из PL/SQL UTL_SMTP / 14 сообщений из 14, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]