Procedure for sending mails

This commit is contained in:
Jan Kvetina 2022-01-05 21:15:31 +01:00
parent 056b3bda88
commit 9bf82ae162
2 changed files with 216 additions and 0 deletions

View File

@ -32,6 +32,14 @@ CREATE OR REPLACE PACKAGE app_actions AS
settings_package CONSTANT VARCHAR2(30) := 'SETT';
settings_prefix CONSTANT VARCHAR2(30) := 'get_';
-- for sending emails
smtp_from CONSTANT VARCHAR2(200) := '';
smtp_username CONSTANT VARCHAR2(50) := NULL;
smtp_password CONSTANT VARCHAR2(50) := NULL;
smtp_host CONSTANT VARCHAR2(50) := '';
smtp_port CONSTANT NUMBER(4) := 25;
smtp_timeout CONSTANT NUMBER(2) := 20;
@ -235,5 +243,23 @@ CREATE OR REPLACE PACKAGE app_actions AS
--
PROCEDURE refresh_user_source_views;
--
-- Send UTF_8 email with compressed attachements
--
PROCEDURE send_mail (
in_to VARCHAR2,
in_subject VARCHAR2,
in_body CLOB,
in_cc VARCHAR2 := NULL,
in_bcc VARCHAR2 := NULL,
in_from VARCHAR2 := NULL,
in_attach_name VARCHAR2 := NULL,
in_attach_mime VARCHAR2 := NULL,
in_attach_data CLOB := NULL,
in_compress BOOLEAN := FALSE
);
END;
/

View File

@ -816,5 +816,195 @@ CREATE OR REPLACE PACKAGE BODY app_actions AS
app.raise_error();
END;
PROCEDURE send_mail (
in_to VARCHAR2,
in_subject VARCHAR2,
in_body CLOB,
in_cc VARCHAR2 := NULL,
in_bcc VARCHAR2 := NULL,
in_from VARCHAR2 := NULL,
in_attach_name VARCHAR2 := NULL,
in_attach_mime VARCHAR2 := NULL,
in_attach_data CLOB := NULL,
in_compress BOOLEAN := FALSE
) AS
boundary CONSTANT VARCHAR2(80) := '-----5b9d8059445a8eb8c025f159131f02d94969a12c16363d4dec42e893b374cb85-----';
--
reply UTL_SMTP.REPLY;
conn UTL_SMTP.CONNECTION;
--
blob_content BLOB;
blob_gzipped BLOB;
blob_amount BINARY_INTEGER := 6000;
blob_offset PLS_INTEGER := 1;
buffer VARCHAR2(24000);
buffer_raw RAW(6000);
--
FUNCTION quote_encoding (
in_text VARCHAR2
)
RETURN VARCHAR2 AS
BEGIN
RETURN '=?UTF-8?Q?' || REPLACE(
UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.QUOTED_PRINTABLE_ENCODE(
UTL_RAW.CAST_TO_RAW(in_text))), '=' || UTL_TCP.CRLF, '') || '?=';
END;
--
FUNCTION quote_address (
in_address VARCHAR2,
in_strip_name BOOLEAN := FALSE
)
RETURN VARCHAR2 AS
in_found PLS_INTEGER;
BEGIN
IF in_strip_name THEN
RETURN REGEXP_REPLACE(in_address, '.*\s?<(\S+)>$', '\1');
ELSE
in_found := REGEXP_INSTR(in_address, '\s?<\S+@\S+\.\S{2,6}>$');
IF in_found > 1 THEN
RETURN quote_encoding(RTRIM(SUBSTR(in_address, 1, in_found))) || SUBSTR(in_address, in_found);
ELSE
RETURN in_address;
END IF;
END IF;
END;
--
FUNCTION clob_to_blob (
in_clob CLOB
)
RETURN BLOB AS
out_blob BLOB;
--
v_file_size INTEGER := DBMS_LOB.LOBMAXSIZE;
v_dest_offset INTEGER := 1;
v_src_offset INTEGER := 1;
v_blob_csid NUMBER := DBMS_LOB.DEFAULT_CSID;
v_lang_context NUMBER := DBMS_LOB.DEFAULT_LANG_CTX;
v_warning INTEGER;
v_length NUMBER;
BEGIN
DBMS_LOB.CREATETEMPORARY(out_blob, TRUE);
DBMS_LOB.CONVERTTOBLOB(out_blob, in_clob, v_file_size, v_dest_offset, v_src_offset, v_blob_csid, v_lang_context, v_warning);
RETURN out_blob;
END;
--
PROCEDURE split_addresses (
in_out_conn IN OUT NOCOPY UTL_SMTP.CONNECTION,
in_to IN VARCHAR2
)
AS
BEGIN
FOR i IN (
SELECT LTRIM(RTRIM(REGEXP_SUBSTR(in_to, '[^;,]+', 1, LEVEL))) AS address
FROM DUAL
CONNECT BY REGEXP_SUBSTR(in_to, '[^;,]+', 1, LEVEL) IS NOT NULL)
LOOP
UTL_SMTP.RCPT(in_out_conn, quote_address(i.address, TRUE));
END LOOP;
END;
BEGIN
app.log_module(in_to, in_subject, in_cc, in_bcc, in_attach_name);
-- connect to SMTP server
reply := UTL_SMTP.OPEN_CONNECTION(smtp_host, smtp_port, conn, smtp_timeout);
UTL_SMTP.HELO(conn, smtp_host);
IF smtp_username IS NOT NULL THEN
UTL_SMTP.COMMAND(conn, 'AUTH LOGIN');
UTL_SMTP.COMMAND(conn, UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(smtp_username)));
IF smtp_password IS NOT NULL THEN
UTL_SMTP.COMMAND(conn, UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(smtp_password)));
END IF;
END IF;
-- prepare headers
UTL_SMTP.MAIL(conn, quote_address(COALESCE(in_from, smtp_from), TRUE));
-- handle multiple recipients
split_addresses(conn, in_to);
--
IF in_cc IS NOT NULL THEN
split_addresses(conn, in_cc);
END IF;
--
IF in_bcc IS NOT NULL THEN
split_addresses(conn, in_bcc);
END IF;
-- continue with headers
UTL_SMTP.OPEN_DATA(conn);
--
UTL_SMTP.WRITE_DATA(conn, 'Date: ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'From: ' || quote_address(COALESCE(in_from, smtp_from)) || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'To: ' || quote_address(in_to) || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Subject: ' || quote_encoding(in_subject) || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Reply-To: ' || in_from || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'MIME-Version: 1.0' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Content-Type: multipart/mixed; boundary="' || boundary || '"' || UTL_TCP.CRLF || UTL_TCP.CRLF);
-- prepare body content
IF in_body IS NOT NULL THEN
UTL_SMTP.WRITE_DATA(conn, '--' || boundary || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Content-Type: ' || 'text/html' || '; charset="utf-8"' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Content-Transfer-Encoding: base64' || UTL_TCP.CRLF || UTL_TCP.CRLF);
--
FOR i IN 0 .. TRUNC((DBMS_LOB.GETLENGTH(in_body) - 1) / 12000) LOOP
UTL_SMTP.WRITE_RAW_DATA(conn, UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(DBMS_LOB.SUBSTR(in_body, 12000, i * 12000 + 1))));
END LOOP;
--
UTL_SMTP.WRITE_DATA(conn, UTL_TCP.CRLF || UTL_TCP.CRLF);
END IF;
-- prepare attachment
IF in_attach_name IS NOT NULL AND in_compress THEN
-- compress attachment
UTL_SMTP.WRITE_DATA(conn, '--' || boundary || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Content-Transfer-Encoding: base64' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Content-Type: ' || 'application/octet-stream' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Content-Disposition: attachment; filename="' || in_attach_name || '.gz"' || UTL_TCP.CRLF || UTL_TCP.CRLF);
--
blob_content := clob_to_blob(in_attach_data);
DBMS_LOB.CREATETEMPORARY(blob_gzipped, TRUE, DBMS_LOB.CALL);
DBMS_LOB.OPEN(blob_gzipped, DBMS_LOB.LOB_READWRITE);
--
UTL_COMPRESS.LZ_COMPRESS(blob_content, blob_gzipped, quality => 8);
--
WHILE blob_offset <= DBMS_LOB.GETLENGTH(blob_gzipped) LOOP
DBMS_LOB.READ(blob_gzipped, blob_amount, blob_offset, buffer_raw);
UTL_SMTP.WRITE_RAW_DATA(conn, UTL_ENCODE.BASE64_ENCODE(buffer_raw));
blob_offset := blob_offset + blob_amount;
END LOOP;
DBMS_LOB.FREETEMPORARY(blob_gzipped);
--
UTL_SMTP.WRITE_DATA(conn, UTL_TCP.CRLF || UTL_TCP.CRLF);
ELSIF in_attach_name IS NOT NULL THEN
-- regular attachment
UTL_SMTP.WRITE_DATA(conn, '--' || boundary || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Content-Transfer-Encoding: base64' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Content-Type: ' || in_attach_mime || '; name="' || in_attach_name || '"' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(conn, 'Content-Disposition: attachment; filename="' || in_attach_name || '"' || UTL_TCP.CRLF || UTL_TCP.CRLF);
--
FOR i IN 0 .. TRUNC((DBMS_LOB.GETLENGTH(in_attach_data) - 1) / 12000) LOOP
UTL_SMTP.WRITE_RAW_DATA(conn, UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(DBMS_LOB.SUBSTR(in_attach_data, 12000, i * 12000 + 1))));
END LOOP;
--
UTL_SMTP.WRITE_DATA(conn, UTL_TCP.CRLF || UTL_TCP.CRLF);
END IF;
-- close
UTL_SMTP.WRITE_DATA(conn, '--' || boundary || '--' || UTL_TCP.CRLF);
UTL_SMTP.CLOSE_DATA(conn);
UTL_SMTP.QUIT(conn);
EXCEPTION
WHEN UTL_SMTP.TRANSIENT_ERROR OR UTL_SMTP.PERMANENT_ERROR THEN
BEGIN
UTL_SMTP.QUIT(conn);
EXCEPTION
WHEN UTL_SMTP.TRANSIENT_ERROR OR UTL_SMTP.PERMANENT_ERROR THEN
NULL;
END;
END;
END;
/