The following PL/SQL program that sends email, which uses basic authentication to authenticate to sendgrid, was working until yesterday.
create or replace PROCEDURE ENVIA_EMAIL( p_remetente IN VARCHAR2
,p_destinatario IN VARCHAR2
,p_titulo_email IN VARCHAR2
,p_mensagem IN VARCHAR2
,p_dsc_arquivo_atachado1 IN VARCHAR2 DEFAULT NULL
,p_tipo_arquivo1 IN VARCHAR2 DEFAULT 'TEXT'
,p_dsc_arquivo_atachado2 IN VARCHAR2 DEFAULT NULL
,p_tipo_arquivo2 IN VARCHAR2 DEFAULT 'TEXT'
,p_dsc_arquivo_atachado3 IN VARCHAR2 DEFAULT NULL
,p_tipo_arquivo3 IN VARCHAR2 DEFAULT 'TEXT'
,p_username IN VARCHAR2
,p_password IN VARCHAR2) IS
--
-- Declaracao de variaveis
w_usuario NUMBER(10);
w_smtp_ip VARCHAR2(30) := 'smtp.sendgrid.net';
w_smtp_porta NUMBER := 587;
boundary CONSTANT VARCHAR2(256) := 'CES.Boundary.DACA587499938897';
w_conexao UTL_SMTP.CONNECTION;
w_mensagem VARCHAR2(30000);
w_nova_linha VARCHAR(2) := chr(13)||chr(10);
wrk_local NUMBER;
w_destinatario VARCHAR2(500);
w_destinatario_todo VARCHAR2(500);
w_destin_todo_fixo VARCHAR2(500);
TYPE varchar2_table IS TABLE OF VARCHAR2(256) INDEX BY BINARY_INTEGER;
w_contador BINARY_INTEGER;
function subject_encode(s_string varchar2) return varchar2 is
temp_subject varchar2(6000);
lengthsubject pls_integer:= 40;
w_nova_linha VARCHAR(2) := chr(13)||chr(10);
count_length pls_integer;
begin
count_length:=CEIL(LENGTH(s_string)/lengthsubject);
temp_subject:='=?iso-8859-1?B?' ||
utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(substr(s_string,1,lengthsubject ))))|| '?=';
for i in 2..count_length loop
temp_subject:=temp_subject||'=?iso-8859-1?B?'||
utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw( SUBSTR(s_string,1+(lengthsubject*(i-1)) ,lengthsubject ))))|| '?=';
end loop;
return temp_subject;
end;
BEGIN
w_destinatario_todo := p_destinatario||';';
w_destin_todo_fixo := p_destinatario;
LOOP
w_destinatario := substr(w_destinatario_todo,1,(instr(w_destinatario_todo,';')) - 1);
w_destinatario_todo := substr(w_destinatario_todo,(instr(w_destinatario_todo,';') + 1));
IF w_destinatario IS NOT NULL THEN
-- Abrindo Conexao SMTP e HTTP
w_conexao := utl_smtp.open_connection(w_smtp_ip,w_smtp_porta);
-- Comunicando SMTP
utl_smtp.helo(w_conexao, w_smtp_ip);
-- Autenticacao INICIO
utl_smtp.command(w_conexao,'AUTH LOGIN');
utl_smtp.command(w_conexao,utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(('username')))));
utl_smtp.command(w_conexao,utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(('password')))));
utl_smtp.mail(w_conexao,('<'||'myusername#myhost.com.br'||'>'));
utl_smtp.rcpt(w_conexao,('<'||w_destinatario||'>'));
utl_smtp.open_data (w_conexao);
-- Criando Cabeca do E-mail
w_mensagem := 'Date: '||TO_CHAR(SYSDATE,'dd Mon yy hh24:mi:ss')||w_nova_linha||
'From: '||p_remetente||w_nova_linha||
'To: '||w_destin_todo_fixo||w_nova_linha||
'Subject: '||subject_encode(p_titulo_email)||w_nova_linha;
--- 'To: '||w_destin_todo_fixo||w_nova_linha;
-- w_mensagem := w_mensagem || 'Mime-Version: 1.0' || w_nova_linha ||
-- 'Content-Type: multipart/mixed; boundary="' || boundary || '"' || w_nova_linha || w_nova_linha; --||
utl_smtp.write_data(w_conexao,w_mensagem);
--
IF p_mensagem IS not NULL THEN
w_mensagem := '--' || boundary || w_nova_linha ||
'Content-Type: text/html; charset=iso-8859-1' || w_nova_linha ||
'Content-Transfer-Encoding: base64' || w_nova_linha || w_nova_linha;
utl_smtp.write_data(w_conexao,w_mensagem);
utl_smtp.write_data(w_conexao,utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(p_mensagem)))|| w_nova_linha);
END IF;
-- Append the final boundary line
-- w_mensagem := w_nova_linha || '--' || boundary || '--' || w_nova_linha;
-- utl_smtp.write_data(w_conexao,w_mensagem);
-- Fechando conexao SMTP
utl_smtp.close_data(w_conexao);
utl_smtp.quit(w_conexao);
-- Insere um registro de emails.
BEGIN
w_usuario := substr(USER,2,50);
EXCEPTION
WHEN OTHERS THEN
w_usuario := 0;
END;
END IF;
IF w_destinatario_todo IS NULL THEN
EXIT;
END IF;
END LOOP;
END;
Yesterday sendgrid stopped supporting basic authentication. Now sendgrid require that an api key is used to authenticate.
I followed sendgrid instructions and created an api key via UI portal. Also, I replaced the username and password in the e-mail program with the values of "apikey"(username) and "actualKey"(password) according to sendgrid documentation as illustrated below.
utl_smtp.command(w_conexao,utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(('apikey')))));
utl_smtp.command(w_conexao,utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(('Sdyg.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')))));
However, I am getting the following error when attempting to send an e-mail.
"The provided authorization grant is invalid, expired or revoked"
Any advice on how I could fix that?
The issue was that a line break is added when the following is executed
select utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(('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')))) from dual;
the output is
U0cuNkU5TExrMHNUamZ0YzZxbG04ZXpnUS5raXhJd2RTSWV0RGE4d0ZUYnNNZHFf
Z1hWeVgxM3dtSHhGdlU5SHZmOTlv
To remove that line break, I modified that statement to look like that
select replace(replace(replace(utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(('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')))),chr(10),' '),chr(13),' '),' ','') from dual;
the output is
U0cuNkU5TExrMHNUamZ0YzZxbG04ZXpnUS5raXhJd2RTSWV0RGE4d0ZUYnNNZHFfZ1hWeVgxM3dtSHhGdlU5SHZmOTlv
Related
Need help in sending excel file as an attachment which is stored in directory of a server.
I have a procedure which writes a file to server and need to send it as as attachement with HTML body message which includes multiple tables and excel attachement. but i am quite stuck in attachment part for now!.
CREATE OR REPLACE PROCEDURE p_send_email_with_attach as
file_handle utl_file.file_type;
output VARCHAR2(32767);
attachment_text VARCHAR2(32767);
v_dir VARCHAR2(300) := 'RPT_DIR';
v_recipients VARCHAR2(2000) := 'abc#gmail.com';
v_sub VARCHAR2(300) := 'Hello';
v_smtp_server VARCHAR2(255) := 'mailhost';
conn utl_smtp.connection;
c_mime_boundary CONSTANT VARCHAR2(256) := 'the boundary can be almost anything';
v_from_name VARCHAR2(100) := 'xyz#yahoo.com';
BEGIN
file_handle := utl_file.fopen(
location => v_dir,
filename => 'My_First_CSV.CSV',
open_mode => 'R'
);
LOOP
BEGIN
utl_file.get_line(
file_handle,
output
); -- we read the file, line by line
attachment_text := attachment_text || utl_tcp.crlf; --and store every line in
the attachment_text variable, separated by a ?new line? character.
EXCEPTION
WHEN no_data_found THEN
EXIT;
END;
END LOOP;
utl_file.fclose(file_handle);
conn := utl_smtp.open_connection('mailhost');
utl_smtp.helo(conn, 'mailhost');
utl_smtp.mail(conn, v_from_name);
FOR cur_r IN (
SELECT level AS id, regexp_substr(email_id, '[^;]+', 1, level) AS v_recipient
FROM
email_address
CONNECT BY regexp_substr( email_id, '[^;]+', 1, level) IS NOT NULL
) LOOP
utl_smtp.rcpt(conn,cur_r.v_recipient);
END LOOP;
UTL_SMTP.write_data(conn, '--' || c_mime_boundary || UTL_TCP.crlf);
UTL_SMTP.write_data(conn, 'Content-Type: text/plain' || UTL_TCP.crlf);
UTL_SMTP.write_data(
conn,
'Content-Disposition: attachment; filename="' || 'My_First_CSV.CSV' || '"' || UTL_TCP.crlf
);
utl_smtp.open_data(conn);
EXCEPTION
WHEN OTHERS THEN
raise_application_error(
-20001,
'Error: ' || sqlerrm
);
END;
i beleive i am missing something while writing, kindly help in fixing this. thanks :)
i make a form i want when i click on button it send email i get a code for email from internet
CREATE OR REPLACE FUNCTION FSC.SEND_MAIL
(pIssuer IN VARCHAR2,
pReceiver IN VARCHAR2,
pSender IN VARCHAR2,
pSubject IN VARCHAR2,
pMessage IN VARCHAR2) RETURN VARCHAR2 IS
c utl_smtp.connection;
respuesta utl_smtp.reply;
pServer VARCHAR2(50) := '192.168.0.6';
BEGIN
-- Open the connection to the mail server
c := utl_smtp.open_connection(pServer);
respuesta := utl_smtp.helo(c, pServer);
-- Start the Issuer mail.
respuesta := utl_smtp.mail(c, pSender);
-- Starts Receiver
respuesta := utl_smtp.rcpt(c, pReceiver);
respuesta := utl_smtp.open_data(c);
-- Enter the email header
utl_smtp.write_data(c, 'From: ' || pIssuer || utl_tcp.CRLF);
utl_smtp.write_data(c, 'To: ' || pReceiver || utl_tcp.CRLF);
-- Enter the Subject
utl_smtp.write_data(c, 'Subject: ' || pSubject || utl_tcp.CRLF);
-- Write the message text.
utl_smtp.write_data(c, utl_tcp.CRLF || pMessage);
utl_smtp.write_data(c, utl_tcp.CRLF || '.');
respuesta := utl_smtp.close_data(c);
-- Close connection
respuesta := utl_smtp.quit(c);
RETURN '0';
EXCEPTION
WHEN utl_smtp.transient_error OR utl_smtp.permanent_error THEN
utl_smtp.quit(c);
RETURN sqlerrm;
--raise_application_error(-20000,
-- 'The sending of the email has failed by returning the following error: ' || sqlerrm);
WHEN OTHERS THEN
RETURN sqlerrm;
END;
/
i make this function in sql it and it was successfully run but when i execute then no email send to my desire address
declare
begin
dbms_output.put_line(SEND_MAIL('usmanafb#ctm.com.pk','usmanafb#ctm.com.pk','usmanafb#ctm.com.pk','Testing','email message'));
end;
i use my local exchange for eamil sending and the ip address of that server is 192.168.0.6
the second issue in this code when i make same function in Oracle 10 g forms then it give me this error
utl_tcp.CRLF is can not directly acces remote package
I use this general procedure to send out mails. It also supports attachment (plain text only) and mails are not limited to 32767 characters.
If you don't need attachments at all, it should be no problem for you to remove it.
PRIORITY_HIGH CONSTANT INTEGER := 1;
PRIORITY_NORMAL CONSTANT INTEGER := 3;
PRIORITY_LOW CONSTANT INTEGER := 5;
PROCEDURE SendMail(
Subject IN VARCHAR2,
Message IN OUT CLOB,
ToMail IN VARCHAR2,
FromMail IN VARCHAR2, FromName IN VARCHAR2,
Attachment IN OUT CLOB, FileName IN VARCHAR2,
Priority IN INTEGER DEFAULT PRIORITY_NORMAL) IS
MIME_BOUNDARY CONSTANT VARCHAR2(50) := '====Multipart.Boundary.689464861147414354====';
MIME_MIXED CONSTANT VARCHAR2(50) := 'multipart/mixed;';
MIME_TEXT CONSTANT VARCHAR2(50) := 'text/plain;';
MIME_HTML CONSTANT VARCHAR2(50) := 'text/html;';
MAIL_HOST CONSTANT VARCHAR2(50) := '192.168.0.6'; -- try also 'mailhost'
con UTL_SMTP.connection;
ret UTL_SMTP.reply;
Charset VARCHAR2(20);
Footer VARCHAR2(1000);
ClobLen PLS_INTEGER;
amount BINARY_INTEGER := 8192;
buffer VARCHAR2(16384);
offset PLS_INTEGER := 1;
isHTML BOOLEAN := REGEXP_LIKE(DBMS_LOB.SUBSTR(Message, 1000, 1), '<(html)|(body)', 'i');
BEGIN
SELECT UTL_I18N.MAP_CHARSET(VALUE)
INTO Charset
FROM V$NLS_PARAMETERS
WHERE PARAMETER = 'NLS_CHARACTERSET';
-- setup mail header
con := UTL_SMTP.OPEN_CONNECTION(MAIL_HOST, 25);
ret := UTL_SMTP.helo(con, SYS_CONTEXT('USERENV', 'DB_DOMAIN')); -- assuming your database is in the same domain as your mail server
ret := UTL_SMTP.Mail(con, FromMail);
ret := UTL_SMTP.rcpt(con, ToMail);
-- simply call "UTL_SMTP.rcpt(con, ...);" again in order to add further recipient
ret := UTL_SMTP.open_data(con);
IF FromName IS NOT NULL THEN
UTL_SMTP.write_data(con, 'From: "'||FromName||'" <'||FromMail||'>'||Utl_Tcp.CRLF);
ELSE
UTL_SMTP.write_data(con, 'From: <'||FromMail||'>'||Utl_Tcp.CRLF);
END IF;
UTL_SMTP.write_data(con, 'To: <'||ToMail||'>'||Utl_Tcp.CRLF);
-- UTL_SMTP.write_data(con, 'Cc: <'||CcMail||'>'||Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, 'Subject: '||Subject||Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, 'X-Priority: '||Priority||Utl_Tcp.CRLF);
IF Attachment IS NOT NULL AND FileName IS NOT NULL THEN
UTL_SMTP.write_data(con, 'Mime-Version: 1.0' || Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, 'Content-Type: '||MIME_MIXED||' boundary="'||MIME_BOUNDARY||'"' || Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, 'This is a multipart message in MIME format.' || Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, '--'||MIME_BOUNDARY || Utl_Tcp.CRLF);
END IF;
Footer := 'Message from '||SYS_CONTEXT('USERENV', 'DB_NAME')||' sent at '||TO_CHAR(SYSDATE,'yyyy-mm-dd hh24:mi:ss');
IF isHTML THEN
UTL_SMTP.write_data(con, 'Content-type: '||MIME_HTML||' charset='||Charset || Utl_Tcp.CRLF);
Message := REPLACE(message, '</body>', '<p>'||Footer||'</p></body>');
ELSE
UTL_SMTP.write_data(con, 'Content-type: '||MIME_TEXT||' charset='||Charset || Utl_Tcp.CRLF);
END IF;
-- Mail Body
UTL_SMTP.write_data(con, Utl_Tcp.CRLF);
ClobLen := DBMS_LOB.GETLENGTH(Message);
LOOP
EXIT WHEN offset > ClobLen;
DBMS_LOB.READ(Message, amount, offset, BUFFER);
UTL_SMTP.write_raw_data(con, UTL_RAW.cast_to_raw(BUFFER));
offset := offset + amount;
END LOOP;
UTL_SMTP.write_data(con, Utl_Tcp.CRLF);
IF NOT isHTML THEN
UTL_SMTP.write_data(con, Utl_Tcp.CRLF || Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, Footer);
UTL_SMTP.write_data(con, Utl_Tcp.CRLF);
END IF;
IF Attachment IS NOT NULL AND FileName IS NOT NULL THEN
-- Mail Attachment
UTL_SMTP.write_data(con, Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, '--'||MIME_BOUNDARY || Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, 'Content-Type: '||MIME_TEXT||' name="'||Filename||'"'|| Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, 'Content-Disposition: attachment; filename="'||Filename||'"'|| Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, Utl_Tcp.CRLF);
offset := 1;
ClobLen := DBMS_LOB.GETLENGTH(Attachment);
LOOP
EXIT WHEN offset > ClobLen;
DBMS_LOB.READ(Attachment, amount, offset, BUFFER);
UTL_SMTP.write_raw_data(con, Utl_Raw.cast_to_raw(BUFFER));
offset := offset + amount;
END LOOP;
UTL_SMTP.write_data(con, Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, '--'||MIME_BOUNDARY||'--' || Utl_Tcp.CRLF);
END IF;
-- finish mail
ret := UTL_SMTP.close_data(con);
ret := UTL_SMTP.quit(con);
EXCEPTION
WHEN UTL_SMTP.TRANSIENT_ERROR OR UTL_SMTP.PERMANENT_ERROR THEN
UTL_SMTP.quit(con);
RAISE;
END SendMail;
Just a note, do not miss the UTL_SMTP.write_data(con, UTL_TCP.CRLF) lines. They are looking redundant, however most of them are required! Also courtesy message like "This is a multipart message in MIME format." is needed for proper display of your mail in case of attachments.
If you still face problems make a dbms_output.put_line(ret.code||': '||ret.text); after each function call on UTL_SMTP.
Here the same procedure but reduced to your needs:
FUNCTION SendMail(
Subject IN VARCHAR2,
Message IN VARCHAR2,
FromMail IN VARCHAR2, FromName IN VARCHAR2,
ToMail IN VARCHAR2) RETURN VARCHAR2 IS
MIME_TEXT CONSTANT VARCHAR2(50) := 'text/plain;';
MIME_HTML CONSTANT VARCHAR2(50) := 'text/html;';
MAIL_HOST CONSTANT VARCHAR2(50) := '192.168.0.6'; -- try also 'mailhost'
con UTL_SMTP.connection;
ret UTL_SMTP.reply;
Charset VARCHAR2(20);
isHTML BOOLEAN := REGEXP_LIKE(DBMS_LOB.SUBSTR(Message, 1000, 1), '<(html)|(body)', 'i');
BEGIN
SELECT UTL_I18N.MAP_CHARSET(VALUE)
INTO Charset
FROM V$NLS_PARAMETERS
WHERE PARAMETER = 'NLS_CHARACTERSET';
-- setup mail header
con := UTL_SMTP.OPEN_CONNECTION(MAIL_HOST, 25);
ret := UTL_SMTP.helo(con, SYS_CONTEXT('USERENV', 'DB_DOMAIN')); -- assuming your database is in the same domain as your mail server
ret := UTL_SMTP.Mail(con, FromMail);
ret := UTL_SMTP.rcpt(con, ToMail);
ret := UTL_SMTP.open_data(con);
UTL_SMTP.write_data(con, 'From: "'||FromName||'" <'||FromMail||'>'||Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, 'To: <'||ToMail||'>'||Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, 'Subject: '||Subject||Utl_Tcp.CRLF);
UTL_SMTP.write_data(con, 'X-Priority: 3'||Utl_Tcp.CRLF);
IF isHTML THEN
UTL_SMTP.write_data(con, 'Content-type: '||MIME_HTML||' charset='||Charset || Utl_Tcp.CRLF);
ELSE
UTL_SMTP.write_data(con, 'Content-type: '||MIME_TEXT||' charset='||Charset || Utl_Tcp.CRLF);
END IF;
-- Mail Body
UTL_SMTP.write_data(con, Utl_Tcp.CRLF);
UTL_SMTP.write_raw_data(con, UTL_RAW.cast_to_raw(Message));
UTL_SMTP.write_data(con, Utl_Tcp.CRLF);
-- finish mail
ret := UTL_SMTP.close_data(con);
ret := UTL_SMTP.quit(con);
RETURN '0';
EXCEPTION
WHEN UTL_SMTP.TRANSIENT_ERROR OR UTL_SMTP.PERMANENT_ERROR THEN
UTL_SMTP.quit(con);
RETURN SQLERRM;
END SendMail;
I have a .txt file in which I have stored my data Semicolon-separated.
The file is stored at backend in a Clob.
File:
ABCD;HEAD; 1.0;00204;18.05.2015;00000000;00000000000.00;18.05.2015
I need to send this text file as an attachment via Stored Procedure in Oracle.
Problem:
I get this encoded file attached in the Email:
4546554C3B484541443B20202020202020312E303B30303634323B31382E30352E323031353B30303030303030313B30303030303030303032302E30303B31382E30352E323031350A4546554C3B444154413B20202020202020312E303B4546554C3B303030543130303030303335303B313B31382E30352E323031353B303030303032302C3030302E30303B504B523B3132363435363135313231313B303235303533313030310D0A
Procedure:
CREATE OR REPLACE PROCEDURE SENDMAIL_ATT
(
MSG_FROM VARCHAR2,
MSG_TO VARCHAR2,
MSG_SUBJECT VARCHAR2,
MSG_TEXT VARCHAR2,
MSG_ATT CLOB,
ATT_FILENAME VARCHAR2
) IS
V_MAILHOST VARCHAR2(50) := 'mail_server';
V_PORT NUMBER(2) := 25;
V_HELO VARCHAR2(50) := 'localhost';
C UTL_TCP.CONNECTION;
RC INTEGER;
CRLF VARCHAR2(2) := CHR(13) || CHR(10);
MESG VARCHAR2(32767);
V_BUFFER_SIZE CONSTANT PLS_INTEGER := 4000;
V_BUFFER_NEXT PLS_INTEGER := 4000;
V_BODY_BUFFER VARCHAR2(8000 CHAR);
BEGIN
C := UTL_TCP.OPEN_CONNECTION(V_MAILHOST, V_PORT); -- open the SMTP port
DBMS_OUTPUT.PUT_LINE(UTL_TCP.GET_LINE(C, TRUE));
RC := UTL_TCP.WRITE_LINE(C, 'HELO ' || V_HELO);
DBMS_OUTPUT.PUT_LINE(UTL_TCP.GET_LINE(C, TRUE));
RC := UTL_TCP.WRITE_LINE(C, 'MAIL FROM: ' || MSG_FROM); ----- MAIL BOX SENDING THE EMAIL
DBMS_OUTPUT.PUT_LINE(UTL_TCP.GET_LINE(C, TRUE));
RC := UTL_TCP.WRITE_LINE(C, 'RCPT TO: ' || MSG_TO); ----- MAIL BOX RECIEVING THE EMAIL
DBMS_OUTPUT.PUT_LINE(UTL_TCP.GET_LINE(C, TRUE));
RC := UTL_TCP.WRITE_LINE(C, 'DATA'); ----- EMAIL MESSAGE BODY START
DBMS_OUTPUT.PUT_LINE(UTL_TCP.GET_LINE(C, TRUE));
RC := UTL_TCP.WRITE_LINE(C,'Date: ' ||
TO_CHAR(SYSDATE, 'dd Mon yy hh24:mi:ss'));
RC := UTL_TCP.WRITE_LINE(C, 'From: ' || MSG_FROM);
RC := UTL_TCP.WRITE_LINE(C, 'MIME-Version: 1.0');
RC := UTL_TCP.WRITE_LINE(C, 'To: ' || MSG_TO);
RC := UTL_TCP.WRITE_LINE(C, 'Subject: ' || MSG_SUBJECT);
RC := UTL_TCP.WRITE_LINE(C, 'Content-Type: multipart/mixed;'); ----- INDICATES THAT THE BODY CONSISTS OF MORE THAN ONE PART
RC := UTL_TCP.WRITE_LINE(C, ' boundary="-----SECBOUND"'); ----- SEPERATOR USED TO SEPERATE THE BODY PARTS
RC := UTL_TCP.WRITE_LINE(C); ----- DO NOT REMOVE THIS BLANK LINE - PART OF MIME STANDARD
RC := UTL_TCP.WRITE_LINE(C, '-------SECBOUND');
RC := UTL_TCP.WRITE_LINE(C, 'Content-Type: text/html'); ----- 1ST BODY PART. EMAIL TEXT MESSAGE
RC := UTL_TCP.WRITE_LINE(C, 'Content-Transfer-Encoding: 7bit');
RC := UTL_TCP.WRITE_LINE(C);
RC := UTL_TCP.WRITE_LINE(C, MSG_TEXT); ----- TEXT OF EMAIL MESSAGE
RC := UTL_TCP.WRITE_LINE(C);
RC := UTL_TCP.WRITE_LINE(C, '-------SECBOUND');
RC := UTL_TCP.WRITE_LINE(C, 'Content-Type: text/plain;'); ----- 2ND BODY PART.
RC := UTL_TCP.WRITE_LINE(C, ' name="' || ATT_FILENAME || '"');
RC := UTL_TCP.WRITE_LINE(C, 'Content-Transfer_Encoding: 7bit');
RC := UTL_TCP.WRITE_LINE(C, 'Content-Disposition: attachment;'); ----- INDICATES THAT THIS IS AN ATTACHMENT
RC := UTL_TCP.WRITE_LINE(C, ' filename="' || ATT_FILENAME || '"'); ----- SUGGESTED FILE NAME FOR ATTACHMENT
RC := UTL_TCP.WRITE_LINE(C);
--CLOB
FOR I IN 0 .. FLOOR(DBMS_LOB.GETLENGTH(MSG_ATT) / V_BUFFER_SIZE)
LOOP
DBMS_LOB.READ(MSG_ATT,
V_BUFFER_NEXT,
I * V_BUFFER_SIZE + 1,
V_BODY_BUFFER);
RC := UTL_TCP.WRITE_TEXT(C, V_BODY_BUFFER);
END LOOP;
RC := UTL_TCP.WRITE_LINE(C);
RC := UTL_TCP.WRITE_LINE(C, '.'); ----- EMAIL MESSAGE BODY END
DBMS_OUTPUT.PUT_LINE(UTL_TCP.GET_LINE(C, TRUE));
RC := UTL_TCP.WRITE_LINE(C, 'QUIT'); ----- ENDS EMAIL TRANSACTION
DBMS_OUTPUT.PUT_LINE(UTL_TCP.GET_LINE(C, TRUE));
UTL_TCP.CLOSE_CONNECTION(C); ----- CLOSE SMTP PORT CONNECTION
EXCEPTION
WHEN OTHERS THEN
RAISE;
END SENDMAIL_ATT;
Procedure Call:
declare
-- Local variables here
l_clob clob;
fileName varchar2(100);
begin
SELECT file_name, file_content
INTO fileName, l_clob
FROM my_table
WHERE ID = 141;
SENDMAIL_ATT(MSG_FROM => 'myemail#myDomain.com',
MSG_TO => 'myemail#myDomain.com',
MSG_SUBJECT => 'Test Email',
MSG_TEXT => 'Some Subject',
MSG_ATT => l_clob,
ATT_FILENAME => fileName);
end;
I use this package to send out mails. It works fine with attachments, try it out:
CREATE OR REPLACE PACKAGE BODY SENDMAIL_PKG AS
PRIORITY_HIGH CONSTANT INTEGER := 1;
PRIORITY_NORMAL CONSTANT INTEGER := 3;
PRIORITY_LOW CONSTANT INTEGER := 5;
SMTP_PORT CONSTANT INTEGER := 25;
SMTP_SERVER CONSTANT VARCHAR2(50) := 'mailhost';
SMTP_DOMIAN CONSTANT VARCHAR2(50) := SYS_CONTEXT('USERENV', 'DB_DOMAIN');
MIME_BOUNDARY CONSTANT VARCHAR2(50) := '=====Boundary======';
MIME_MIXED CONSTANT VARCHAR2(50) := 'multipart/mixed;';
MIME_TEXT CONSTANT VARCHAR2(50) := 'text/plain;';
DEFAULT_FROM_NAME CONSTANT VARCHAR2(50) := SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');
DEFAULT_FROM_MAIL CONSTANT VARCHAR2(50) := DEFAULT_FROM_NAME||'#'||SMTP_DOMIAN;
FUNCTION OpenConnection(ToEmail IN VARCHAR2, FromEmail IN VARCHAR2) RETURN UTL_SMTP.connection IS
con UTL_SMTP.connection;
BEGIN
-- setup mail header
con := UTL_SMTP.OPEN_CONNECTION(SMTP_SERVER, SMTP_PORT);
UTL_SMTP.helo(con, SMTP_DOMIAN);
UTL_SMTP.Mail(con, LOWER(FromEmail));
UTL_SMTP.rcpt(con, LOWER(ToEmail));
UTL_SMTP.open_data(con);
RETURN con;
END OpenConnection;
PROCEDURE PutHeader(con IN OUT UTL_SMTP.connection,
Subject IN VARCHAR2, ToEmail IN VARCHAR2,
FromEmail IN VARCHAR2, FromName IN VARCHAR2,
Priority IN INTEGER) IS
BEGIN
UTL_SMTP.write_data(con, 'From: "'||FromName||'" <'||LOWER(FromEmail)||'>'||UTL_TCP.CRLF);
UTL_SMTP.write_data(con, 'To: <'||LOWER(ToEmail)||'>'||UTL_TCP.CRLF);
UTL_SMTP.write_data(con, 'Subject: '||Subject||UTL_TCP.CRLF);
UTL_SMTP.write_data(con, 'X-Priority: '||Priority||UTL_TCP.CRLF);
END PutHeader;
PROCEDURE CloseMail(con IN OUT UTL_SMTP.connection) IS
BEGIN
-- finish mail
UTL_SMTP.close_data(con);
UTL_SMTP.quit(con);
END CloseMail;
-- Append default footer at the end of mail
PROCEDURE PutFooter(con IN OUT UTL_SMTP.connection) IS
sender VARCHAR2(1000);
BEGIN
-- put footer to end of mail
UTL_SMTP.write_data(con, UTL_TCP.CRLF || UTL_TCP.CRLF);
sender := SYS_CONTEXT('USERENV', 'DB_NAME')||'.'||SYS_CONTEXT('USERENV', 'DB_DOMAIN');
UTL_SMTP.write_data(con, 'Message from '||sender||' sent at '||TO_CHAR(LOCALTIMESTAMP, 'yyyy-mm-dd hh24:mi:ss'));
UTL_SMTP.write_data(con, UTL_TCP.CRLF);
END PutFooter;
-- Send Mail with CLOB attachment
PROCEDURE SendMail(
Subject IN VARCHAR2,
Message IN VARCHAR2,
ToEmail IN VARCHAR2,
Attachment IN CLOB, FilenameAttachment IN VARCHAR2,
FromEmail IN VARCHAR2 DEFAULT DEFAULT_FROM_MAIL,
FromName IN VARCHAR2 DEFAULT DEFAULT_FROM_NAME,
Priority IN INTEGER DEFAULT PRIORITY_NORMAL) IS
con UTL_SMTP.connection;
AttachmentLength PLS_INTEGER;
amount BINARY_INTEGER := 30000;
BUFFER VARCHAR2(32000);
offset PLS_INTEGER := 1;
BEGIN
con := OpenConnection(ToEmail, FromEmail);
PutHeader(con, Subject, ToEmail, FromEmail, FromName, Priority);
UTL_SMTP.write_data(con, 'Mime-Version: 1.0' || UTL_TCP.CRLF);
UTL_SMTP.write_data(con, 'Content-Type: '||MIME_MIXED||' boundary="'||MIME_BOUNDARY||'"' || UTL_TCP.CRLF);
UTL_SMTP.write_data(con, '--'||MIME_BOUNDARY || UTL_TCP.CRLF);
UTL_SMTP.write_data(con, 'Content-type: text/plain' || UTL_TCP.CRLF);
-- Mail Body
UTL_SMTP.write_data(con, UTL_TCP.CRLF);
UTL_SMTP.write_raw_data(con, UTL_RAW.cast_to_raw(Message));
UTL_SMTP.write_data(con, UTL_TCP.CRLF);
PutFooter(con);
-- Mail Attachment
UTL_SMTP.write_data(con, UTL_TCP.CRLF);
UTL_SMTP.write_data(con, '--'||MIME_BOUNDARY || UTL_TCP.CRLF);
UTL_SMTP.write_data(con, 'Content-Type: '||MIME_TEXT||' name="'||FilenameAttachment||'"'|| UTL_TCP.CRLF);
UTL_SMTP.write_data(con, 'Content-Disposition: attachment; filename="'||FilenameAttachment||'"'|| UTL_TCP.CRLF);
UTL_SMTP.write_data(con, UTL_TCP.CRLF);
AttachmentLength := DBMS_LOB.GETLENGTH(Attachment);
LOOP
EXIT WHEN offset > AttachmentLength;
DBMS_LOB.READ(Attachment, amount, offset, BUFFER);
UTL_SMTP.write_raw_data(con, UTL_RAW.cast_to_raw(BUFFER));
offset := offset + amount;
END LOOP;
UTL_SMTP.write_data(con, UTL_TCP.CRLF);
UTL_SMTP.write_data(con, '--'||MIME_BOUNDARY||'--' || UTL_TCP.CRLF);
CloseMail(con);
END SendMail;
-- Send plain Mail without attachment
PROCEDURE SendMail(
Subject IN VARCHAR2,
Message IN VARCHAR2,
ToEmail IN VARCHAR2,
FromEmail IN VARCHAR2 DEFAULT DEFAULT_FROM_MAIL,
FromName IN VARCHAR2 DEFAULT DEFAULT_FROM_NAME,
Priority IN INTEGER DEFAULT PRIORITY_NORMAL) IS
con UTL_SMTP.connection;
BEGIN
con := OpenConnection(ToEmail, FromEmail);
PutHeader(con, Subject, ToEmail, FromEmail, FromName, Priority);
UTL_SMTP.write_data(con, 'Content-type: '||MIME_TEXT ||' charset='||GetCharset||UTL_TCP.CRLF );
UTL_SMTP.write_data(con, UTL_TCP.CRLF);
UTL_SMTP.write_raw_data(con, UTL_RAW.cast_to_raw(Message));
UTL_SMTP.write_data(con, UTL_TCP.CRLF);
PutFooter(con);
CloseMail(con);
END SendMail;
END SENDMAIL_PKG;
How to sent email in Oracle PL/SQL package to multiple receivers? I have below pl/sql procedure within an oracle package, it works only for one receiver. I need to improve it functional to let it can send email to multiple receivers at same time like "To: David Festool; Peter Makita; John Dewalt". Any body can help me out will be great appreciate! Please provide me modified code.
procedure email(p_recip in varchar2,
p_subject in varchar2,
p_message in varchar2) is
c utl_smtp.connection;
msg varchar2(4000);
procedure send_header(name in varchar2, header in varchar2) as
begin
utl_smtp.write_data(c, name || ': ' || header || utl_tcp.crlf);
end;
begin
--Open SMTP connection
c := utl_smtp.open_connection('ExchangeServerName');
-- Write SMTP header
utl_smtp.helo(c, 'ExchangeServerName');
utl_smtp.mail(c, 'Email#MyCompany.on.ca');
utl_smtp.rcpt(c, p_recip);
utl_smtp.open_data(c);
send_header('From', '"Title" <Email#MyCompany.on.ca');
send_header('To', p_recip);
send_header('Subject', p_subject);
send_header('Mime-Version', '1.0');
send_header('Content-Type', 'multipart/mixed; boundary="DMW.Boundary.605592468"');
-- Write MIME boundary line for the message body
msg := utl_tcp.crlf || '--DMW.Boundary.605592468' || utl_tcp.crlf ||
'Content-Type: text/plain' || utl_tcp.crlf ||
'Content-Transfer-Encoding: 7bit' || utl_tcp.crlf ||
utl_tcp.crlf;
utl_smtp.write_data(c, msg);
-- Write message body
utl_smtp.write_data(c, p_message || utl_tcp.crlf);
-- Clean up
utl_smtp.close_data(c);
utl_smtp.quit(c);
exception
when utl_smtp.transient_error or utl_smtp.permanent_error then
begin
utl_smtp.quit(c);
exception
when utl_smtp.transient_error or utl_smtp.permanent_error then
null;
-- When the SMTP server is down or unavailable, we don't have
-- a connection to the server. The QUIT call will raise an
-- exception that we can ignore.
end;
raise_application_error(-20000, 'Failed to send mail due to the following error: ' ||
sqlerrm);
end;
--------------------------------------------------------------
You need to call utl_smtp.rcpt multiple times, once for each recipient; you can't give a list of values in one call.
From the UTL_SMTP.RCPT documentation:
To send a message to multiple recipients, call this routine multiple
times. Each invocation schedules delivery to a single e-mail address.
That means you can't really pass a string of names, unless you're happy to parse the individual addresses out; it would be easier to pass an array of values, probably.
The TO header is a separate issue; if I recall correctly, that is really just for display, and having an address as a rcpt but not in the TO (or CC) header is how BCC is implemented. Citation needed though...
Here's an old AskTom article demonstrating this. jonearles suggestion to use UTL_MAIL should be investigated though.
The format is:
UTL_MAIL.SEND (sender, recipientlist, cc, bcc, subject, Message, mime_type, priority)
The recipientlist, cc, and bcc parameters are all comma-separated lists of recipient, copy to, and blind copy e-mail addresses.
The sender, subject, message, and mime_type parameters are all single item fields.
Just run below procedure with change code:
v_Mail_Host VARCHAR2(50) := 'uacemail.rxcorp.com'; -- your host ip or name
Execute:
begin prc_email_send( 'sohid10#yahoo.com', -- Mail From
'smolla#bd.imshealth.com',---Recipient
'sohidatibd#gmail.com;smokarem#bd.imshealth.com',-- Cc List
'This is mail subject ', 'This is mail body' );
end; /
Procedure Code:
Create or replace procedure prc_email_send(
v_From VARCHAR2,
v_Recipient VARCHAR2,
v_cc_list varchar2,
v_Subject VARCHAR2,
v_Mail_body VARCHAR2
)
is
v_Mail_Host VARCHAR2(50) := 'uacemail.rxcorp.com';
v_Mail_Conn utl_smtp.Connection;
crlf VARCHAR2(2) := chr(13)||chr(10);
CC_parties varchar2(2000);
begin
v_Mail_Conn := utl_smtp.Open_Connection(v_Mail_Host, 25);
utl_smtp.Helo(v_Mail_Conn, v_Mail_Host);
utl_smtp.Mail(v_Mail_Conn, v_From);
utl_smtp.Rcpt(v_Mail_Conn, v_Recipient);
for i in (SELECT LEVEL AS id, REGEXP_SUBSTR(v_cc_list, '[^;]+', 1, LEVEL) AS cc_email_name
FROM dual
CONNECT BY REGEXP_SUBSTR(v_cc_list, '[^;]+', 1, LEVEL) IS NOT NULL) loop
CC_parties := CC_parties||';'|| i.cc_email_name;
utl_smtp.Rcpt(v_Mail_Conn,i.cc_email_name);
end loop;
utl_smtp.Data(v_Mail_Conn,
'Date: ' || to_char(sysdate, 'Dy, DD Mon YYYY hh24:mi:ss') || crlf ||
'From: ' || v_From || crlf ||
'Subject: '|| v_Subject || crlf ||
'To: ' || v_Recipient || crlf ||
'Cc: ' || CC_parties|| crlf ||
'Content-Type: text/html;' ||crlf ||
v_Mail_body);
utl_smtp.Quit(v_mail_conn);
EXCEPTION
WHEN OTHERS THEN
BEGIN
DBMS_OUTPUT.put_line (
SUBSTR (
'Unable to send mail to recipients. Error message: '
|| SQLERRM
|| CHR (10)
|| DBMS_UTILITY.FORMAT_ERROR_BACKTRACE (),
1,255));
UTL_SMTP.quit (v_Mail_Conn);
UTL_TCP.close_all_connections;
EXCEPTION
WHEN UTL_SMTP.transient_error OR UTL_SMTP.permanent_error THEN
NULL;
END;
END;
This is working fine for myself
A client of mine uses Oracle 9i's utl_smtp to send mails out notifications to managers when their employees have made travel requests and they woul like quite a few changes made to the mailouts done.
We're having a lot of problems getting utl_smtp to talk to any smtp server on our network. We've even tried installing free smtp server on the oracle box but it will not spot the mail server running on port 25. The error code is ORA-29278.
So two questions really.
Does anyone have any experience setting up email using Oracle's utl_smtp utility and have any suggestions as to where we might be going wrong.
Does anyone know if it is possible to get utl_smtp to dump text emails to a directory much as you can do if you're using system.net.mail's specifiedpickupdirectory config setting. This would be by far the preferable option.
Thanks, Dan
Looks like the HELO is the problem. Please can we check with a simple testcase...
set serveroutput on
declare
lConnection UTL_SMTP.CONNECTION;
begin
lConnection := UTL_SMTP.OPEN_CONNECTION(your_smtp_server);
DBMS_OUTPUT.PUT_LINE('Opened ok');
UTL_SMTP.HELO(lConnection, your_client_machine_name);
DBMS_OUTPUT.PUT_LINE('HELO ok');
UTL_SMTP.MAIL(lConnection, your_email_address);
UTL_SMTP.RCPT(lConnection, your_email_address);
DBMS_OUTPUT.PUT_LINE('Addressing ok');
end;
/
Looks like we've resolved this.
To answer the two questions.
Double check that the schema calling utl_smtp has execute permissions on sys.utl_smtp, sys.utl_tcp and sys.dbms_lob. Also check that at no time the message being sent is > 32Kb.
No there is no way to get utl_smtp to dump emails to a directory a la system.net.mail.
Thanks to cagcowboy for the help.
Yes, we can telnet to the server.
-- ****** Object: Stored Procedure TRAVELADMIN_DEV.HTML_EMAIL Script Date: 22/08/2008 12:41:02 ******
CREATE PROCEDURE "HTML_EMAIL" (
p_to in varchar2,
p_cc in varchar2,
p_from in varchar2,
p_subject in varchar2,
p_text in varchar2 default null,
p_html in varchar2 default null
)
is
l_boundary varchar2(255) default 'a1b2c3d4e3f2g1';
l_connection utl_smtp.connection;
l_body_html clob := empty_clob; --This LOB will be the email message
l_offset number;
l_ammount number;
l_temp varchar2(32767) default null;
p_smtp_hostname varchar2(30):= 'rockies';
p_smtp_portnum varchar2(2) := '25';
begin
l_connection := utl_smtp.open_connection( p_smtp_hostname, p_smtp_portnum );
utl_smtp.helo( l_connection, p_smtp_hostname );
utl_smtp.mail( l_connection, p_from );
utl_smtp.rcpt( l_connection, p_to );
l_temp := l_temp || 'MIME-Version: 1.0' || chr(13) || chr(10);
l_temp := l_temp || 'To: ' || p_to || chr(13) || chr(10);
IF ((p_cc <> NULL) OR (LENGTH(p_cc) > 0)) THEN
l_temp := l_temp || 'Cc: ' || p_cc || chr(13) || chr(10);
utl_smtp.rcpt( l_connection, p_cc );
END IF;
l_temp := l_temp || 'From: ' || p_from || chr(13) || chr(10);
l_temp := l_temp || 'Subject: ' || p_subject || chr(13) || chr(10);
l_temp := l_temp || 'Reply-To: ' || p_from || chr(13) || chr(10);
l_temp := l_temp || 'Content-Type: multipart/alternative; boundary=' ||
chr(34) || l_boundary || chr(34) || chr(13) ||
chr(10);
----------------------------------------------------
-- Write the headers
dbms_lob.createtemporary( l_body_html, false, 10 );
dbms_lob.write(l_body_html,length(l_temp),1,l_temp);
----------------------------------------------------
-- Write the text boundary
l_offset := dbms_lob.getlength(l_body_html) + 1;
l_temp := '--' || l_boundary || chr(13)||chr(10);
l_temp := l_temp || 'content-type: text/plain; charset=us-ascii' ||
chr(13) || chr(10) || chr(13) || chr(10);
dbms_lob.write(l_body_html,length(l_temp),l_offset,l_temp);
----------------------------------------------------
-- Write the plain text portion of the email
l_offset := dbms_lob.getlength(l_body_html) + 1;
dbms_lob.write(l_body_html,length(p_text),l_offset,p_text);
----------------------------------------------------
-- Write the HTML boundary
l_temp := chr(13)||chr(10)||chr(13)||chr(10)||'--' || l_boundary ||
chr(13) || chr(10);
l_temp := l_temp || 'content-type: text/html;' ||
chr(13) || chr(10) || chr(13) || chr(10);
l_offset := dbms_lob.getlength(l_body_html) + 1;
dbms_lob.write(l_body_html,length(l_temp),l_offset,l_temp);
----------------------------------------------------
-- Write the HTML portion of the message
l_offset := dbms_lob.getlength(l_body_html) + 1;
dbms_lob.write(l_body_html,length(p_html),l_offset,p_html);
----------------------------------------------------
-- Write the final html boundary
l_temp := chr(13) || chr(10) || '--' || l_boundary || '--' || chr(13);
l_offset := dbms_lob.getlength(l_body_html) + 1;
dbms_lob.write(l_body_html,length(l_temp),l_offset,l_temp);
----------------------------------------------------
-- Send the email in 1900 byte chunks to UTL_SMTP
l_offset := 1;
l_ammount := 1900;
utl_smtp.open_data(l_connection);
while l_offset < dbms_lob.getlength(l_body_html) loop
utl_smtp.write_data(l_connection,
dbms_lob.substr(l_body_html,l_ammount,l_offset));
l_offset := l_offset + l_ammount ;
l_ammount := least(1900,dbms_lob.getlength(l_body_html) - l_ammount);
end loop;
utl_smtp.close_data(l_connection);
utl_smtp.quit( l_connection );
dbms_lob.freetemporary(l_body_html);
end;
The OPEN_CONNECTION parameter should be the FQDN or IP address of the server you're connecting to.
The HELO parameter should be the FQDN of the machine you're connecting from.
If this doesn't work, do you know which line it errors on?