--------------------------------------------------
-- Wrapper procedures for utl_smtp, correcting
-- some of the bugs and providing utility functions
--------------------------------------------------
create or replace package mail
is
-- Open the connection and send "helo me"
function open_connection (
--/** Open a mail connection, and prepare to identify the user
-- @author Stanislav Freidin
-- @param host The hostname of the mailsrver, such as
-- 'mail.arsdigita.com'
-- @param port The port on which the mailserver is expecting a
-- connection, usually 25
-- @param mail_conn The mail connection record. Information about
-- the opened connection will be returned here
-- @return The reply code record from the mailserver. The expected reply
-- code is 250.
-- @see {mail.mail_from}, {mail.send_mail}, Oracle's utl_smtp package
--*/
host IN varchar2,
port IN pls_integer,
mail_conn OUT NOCOPY utl_smtp.connection
) return utl_smtp.reply;
-- Send the "mail from:" command
function mail_from (
--/** Identify the user who is sending mail. open_connection
-- must have been called previously
-- @author Stanislav Freidin
-- @param mail_conn The mail connection object, created with
-- open_connection
-- @param email_from The email of the user who is sending mail
-- @return The reply code record from the mailserver. The expected reply
-- code is 250.
-- @see {mail.open_connection}, {mail.rcpt_to}, {mail.send_mail},
-- Oracle's utl_smtp package
--*/
mail_conn IN OUT NOCOPY utl_smtp.connection,
email_from IN varchar2
) return utl_smtp.reply;
-- Send the "rcpt to:" command; forward if neccessary
function rcpt_to (
--/** Identify the recepient of the email. Automatically forward the
-- mail if the recepient has a new address. mail_from must
-- have been called previously
-- @author Stanislav Freidin
-- @param mail_conn The mail connection object, created with
-- open_connection
-- @param email_to The email of the recepient of this email
-- @return The reply code record from the mailserver. The expected reply
-- codes are 250 or 251.
-- @see {mail.open_connection}, {mail.open_data}, {mail.send_mail},
-- Oracle's utl_smtp package
--*/
mail_conn IN OUT NOCOPY utl_smtp.connection,
email_to IN varchar2
) return utl_smtp.reply;
-- Open up the data connection, preparing for the writing of data
function open_data (
--/** Open the data connection, in preparation for sending headers
-- and the body of the message
-- @author Stanislav Freidin
-- @param mail_conn The mail connection object, created with
-- open_connection
-- @param email_to The email of the recepient of this email
-- @return The reply code record from the mailserver. The expected reply
-- code is 250.
-- @see {mail.open_connection}, {mail.write_data_headers}, {mail.send_mail},
-- Oracle's utl_smtp package
--*/
mail_conn IN OUT NOCOPY utl_smtp.connection
) return utl_smtp.reply;
-- Convert a date into email date format (GMT)
function to_email_date (
--/** Convert an Oracle data into a string date compatible with email (GMT)
-- @author Stanislav Freidin
-- @param ora_date The date to convert
-- @return The converted string date
-- @see {mai.write_data_headers}
--*/
ora_date IN date
) return varchar2;
-- Write outgoing headers (date:, to:, from:, subject)
procedure write_data_headers (
--/** Write the email headers into the mail connection. open_data
-- must have been called previously
-- @author Stanislav Freidin
-- @param mail_conn The mail connection object, created with
-- open_connection
-- @param email_from The email of sender of this email
-- @param email_to The email of the recepient of this email
-- @param subject A one-line subject for the message
-- @param date_sent The date when the message has been sent
-- @see {mail.open_connection}, {mail.write_data_headers},
-- {mail.write_data_clob}, {mail.write_data_string}, Oracle's utl_smtp package
--*/
mail_conn IN OUT NOCOPY utl_smtp.connection,
email_from IN varchar2,
email_to IN varchar2,
subject IN varchar2,
date_sent IN date default sysdate
);
-- Write a clob into the mail data connection, in chunks
procedure write_data_clob (
--/** Write a clob into the mail data connection, in chunks of 3000 bytes.
-- open_data must have been called prior to this point
-- @author Stanislav Freidin
-- @param mail_conn The mail connection object, created with
-- open_connection
-- @param clob_loc The clob whose contents will be written into
-- the connection
-- @see {mail.open_connection}, {mail.write_data_headers},
-- {mail.write_data_string}, {mail.send_mail}, Oracle's utl_smtp package
--*/
mail_conn IN OUT NOCOPY utl_smtp.connection,
clob_loc IN OUT NOCOPY clob
);
-- Write a string into the mail data connection
procedure write_data_string (
--/** Write a string into the mail data connection.
-- open_data must have been called prior to this point
-- @author Stanislav Freidin
-- @param mail_conn The mail connection object, created with
-- open_connection
-- @param string_text The string to be written into the connection
-- @see {mail.open_connection}, {mail.write_data_headers},
-- {mail.write_data_clob}, {mail.send_mail}, Oracle's utl_smtp package
--*/
mail_conn IN OUT NOCOPY utl_smtp.connection,
string_text IN varchar2
);
-- Close the data connection
function close_data (
--/** Close the data connection after all the text has been written into
-- the body of the message
-- open_data must have been called prior to this point
-- @author Stanislav Freidin
-- @param mail_conn The mail connection object, created with
-- open_connection
-- @return The reply code record from the mailserver. The expected reply
-- code is 250.
-- @see {mail.open_data}, {mail.close_connection}, Oracle's utl_smtp package
--*/
mail_conn IN OUT NOCOPY utl_smtp.connection
) return utl_smtp.reply;
-- Close the connection, finish mail session
procedure close_connection (
--/** Close the mail connection, thus ending the mail sesssion
-- @author Stanislav Freidin
-- @param mail_conn The mail connection object, created with
-- open_connection
-- @see {mail.open_connection}, Oracle's utl_smtp package
--*/
mail_conn IN OUT NOCOPY utl_smtp.connection
);
-- A simple procedure to send and entire mail message
-- return 't' on success, 'f' on failure
function send_mail (
--/** A simplified procedure to send a complete email message
-- @author Stanislav Freidin
-- @param email_from The sender's email
-- @param email_to The recepient's email
-- @param subject A one-line subject to be sent with the message
-- @param text The body of the message, 4000 characters maximum
-- @param host The hostname of the mailserver, such as
-- 'mail.arsdigita.com'
-- @param port The port on which the mailserver expects a connection,
-- default 25
-- @return 't' if the message was successfully sent, 'f' otherwise
-- @see {mail.open_connection}
--*/
email_from IN varchar2,
email_to IN varchar2,
subject IN varchar2,
text IN varchar2,
host IN varchar2,
port IN pls_integer default 25
) return char;
end mail;
/
show errors
create or replace package body mail
is
function open_connection (
host IN varchar2,
port IN pls_integer,
mail_conn OUT NOCOPY utl_smtp.connection
) return utl_smtp.reply
is
v_mail_reply utl_smtp.reply;
begin
v_mail_reply := utl_smtp.open_connection(host, port, mail_conn);
if v_mail_reply.code <> 220 then
return v_mail_reply;
end if;
return utl_smtp.helo(mail_conn, 'me');
end open_connection;
function mail_from (
mail_conn IN OUT NOCOPY utl_smtp.connection,
email_from IN varchar2
) return utl_smtp.reply
is
v_mail_reply utl_smtp.reply;
begin
return utl_smtp.command(mail_conn, 'mail from:', email_from);
end;
function rcpt_to (
mail_conn IN OUT NOCOPY utl_smtp.connection,
email_to IN varchar2
) return utl_smtp.reply
is
v_mail_reply utl_smtp.reply;
v_email_to varchar2(1000) := email_to;
v_retry_count integer := 0;
begin
for v_retry_count in 0..20 loop
begin
v_mail_reply := utl_smtp.command(mail_conn, 'rcpt to:', v_email_to);
if v_mail_reply.code <> 551 then
return v_mail_reply;
end if;
-- Got the forwarding line, extract the email address and retry
if v_mail_reply.code = 551 then
declare
v_token_info str.token_info;
v_token varchar2(1000);
v_found char(1);
begin
v_token := str.first_token(v_mail_reply.text, v_token_info);
v_found := 'f';
while v_token is not null and v_found = 'f' loop
if instr(v_token, '@') <> 0 then
v_email_to := v_token;
v_found := 't';
end if;
v_token := str.next_token(v_token_info);
end loop;
-- If we could not extract the email, failure
if v_found = 'f' then
return v_mail_reply;
end if;
end;
end if;
exception
when others then
return v_mail_reply;
end;
end loop;
return v_mail_reply;
end;
function open_data (
mail_conn IN OUT NOCOPY utl_smtp.connection
) return utl_smtp.reply
is
begin
return utl_smtp.open_data(mail_conn);
end open_data;
function to_email_date (
ora_date IN date
) return varchar2
is
begin
return initcap(to_char(ora_date, 'DY, DD MON YYYY HH24:MI:SS'));
end to_email_date;
procedure write_data_headers (
mail_conn IN OUT NOCOPY utl_smtp.connection,
email_from IN varchar2,
email_to IN varchar2,
subject IN varchar2,
date_sent IN date default sysdate
)
is
v_newline varchar2(20) := '
';
begin
utl_smtp.write_data(mail_conn,
'Date: ' || to_email_date(date_sent) || v_newline ||
'From: ' || email_from || v_newline ||
'To: ' || email_to || v_newline ||
'Subject:' || subject || v_newline ||
'Content-type: text/plain' || v_newline ||
v_newline
);
end write_data_headers;
procedure write_data_clob (
mail_conn IN OUT NOCOPY utl_smtp.connection,
clob_loc IN OUT NOCOPY clob
)
is
v_clob_length integer;
v_string varchar2(4000);
v_reply utl_smtp.reply;
v_offset integer;
v_amount integer;
begin
v_clob_length := dbms_lob.getlength(clob_loc);
v_offset := 1;
while v_clob_length > 0 loop
if v_clob_length < 3000 then
v_amount := v_clob_length;
else
v_amount := 3000;
end if;
dbms_lob.read(clob_loc, v_amount, v_offset, v_string);
utl_smtp.write_data(mail_conn, v_string);
v_clob_length := v_clob_length - 3000;
v_offset := v_offset + 3000;
end loop;
end write_data_clob;
-- Write a string into the mail data connection
procedure write_data_string (
mail_conn IN OUT NOCOPY utl_smtp.connection,
string_text IN varchar2
)
is
begin
utl_smtp.write_data(mail_conn, string_text);
end write_data_string;
function close_data (
mail_conn IN OUT NOCOPY utl_smtp.connection
) return utl_smtp.reply
is
begin
return utl_smtp.close_data(mail_conn);
end close_data;
procedure close_connection (
mail_conn IN OUT NOCOPY utl_smtp.connection
)
is
begin
utl_smtp.quit(mail_conn);
end close_connection;
-- A simple procedure to send and entire mail message
function send_mail (
email_from IN varchar2,
email_to IN varchar2,
subject IN varchar2,
text IN varchar2,
host IN varchar2,
port IN pls_integer default 25
) return char
is
v_reply utl_smtp.reply;
v_mail_conn utl_smtp.connection;
begin
v_reply := open_connection(host, port, v_mail_conn);
if v_reply.code <> 250 then
return 'f';
end if;
v_reply := mail_from(v_mail_conn, email_from);
if v_reply.code <> 250 then
return 'f';
end if;
v_reply := rcpt_to(v_mail_conn, email_to);
if v_reply.code not in (250, 251) then
return 'f';
end if;
v_reply := open_data(v_mail_conn);
write_data_headers(
v_mail_conn, email_from, email_to, subject, sysdate
);
write_data_string(v_mail_conn, text);
v_reply := close_data(v_mail_conn);
if v_reply.code <> 250 then
return 'f';
end if;
close_connection(v_mail_conn);
return 't';
end send_mail;
end mail;
/
show errors