Send excel file stored in directory using utl_smtp - oracle

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 :)

Related

Fix PL/SQL program that sends e-mail

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

Want to send excel as mail attachment through pl/sql procedure

When I send mail with attachment through procedure using clob to create data.
For small data it's working fine. But for large data it's misbehaving (alignment changing). can you help whether anything needs to done with the code.
create or replace PROCEDURE RPT AS
l_clob clob;
l_bfile bfile;
l_fhandle utl_file.file_type;
l_buffer VARCHAR2(8192);
v_count number;
a_count number;
k_count number;
ka_count number;
currentdate DATE;
non_work_days_count number;
BEGIN
--preparing header
dbms_lob.createtemporary (l_clob, TRUE);
l_clob := l_clob
|| 'Column1'|| ','
|| 'Column2' || ','
|| 'Column3' || ','
|| 'Column4' || ','
|| UTL_TCP.crlf;
for crq in (select col1,col2,col3,col4 from table where id=1 ) loop
/* Prepare Details data using Clob */
l_clob := l_clob
|| to_clob(crq.COl1)|| ','
|| to_clob(crq.COL2) || ','
|| to_clob(crq.COL3) || ','
|| to_clob(crq.COL4) || ','
|| UTL_TCP.crlf;
end loop;
for crq in (select col1,col2,col3,col4 from table where id=2 ) loop
/* Prepare Details data using Clob */
l_clob := l_clob
|| to_clob(crq.COl1)|| ','
|| to_clob(crq.COL2) || ','
|| to_clob(crq.COL3) || ','
|| to_clob(crq.COL4) || ','
|| UTL_TCP.crlf;
end loop;
dbms_output.put_line('Sending mail with attachment ');
ATTACHMENT_SEND(p_to=> 'req#Email.com',
p_from=> 'req#Email.com',
p_subject=> ' Report ',
p_text_msg=>'Hi All',
p_attach_name =>'report_'||sysdate||'.csv',
p_attach_mime =>'text/plain',
p_attach_clob =>l_clob,
p_smtp_host=>'host.com');
dbms_lob.freetemporary(l_clob);
END RPT;
Also I observe when character length reach 32000 it's breaking(leaving remaining data).
The data in the created excel attachment is having alignment issue
After searching a lot, I found a different approach which worked as a gem for me(which can handle lakhs of records). I thought it would help someone someday.
Steps:
1)Created a directory (in which the expected excel is stored).
2)Write the output of the select statement into the above file.
3)Pick the file at above location and send it in the mail attachment.
Note: if any one needs SMTP code part or any assistance let me know
|| concatenation limits you up to 4000 in Oracle SQL while limits to 32000 in PL/SQL. You may try [DBMS_LOB.APPEND][1] procedure to concatenate long CLOBs. So you may try to convert your code to -
CREATE OR REPLACE PROCEDURE RPT AS
l_clob clob;
l_bfile bfile;
l_fhandle utl_file.file_type;
l_buffer VARCHAR2(8192);
v_count number;
a_count number;
k_count number;
ka_count number;
currentdate DATE;
non_work_days_count number;
BEGIN
--preparing header
dbms_lob.createtemporary (l_clob, TRUE);
l_clob := l_clob
|| 'Column1'|| ','
|| 'Column2' || ','
|| 'Column3' || ','
|| 'Column4' || ','
|| UTL_TCP.crlf;
for crq in (select col1,col2,col3,col4 from table where id=1 ) loop
/* Prepare Details data using Clob */
l_clob := DBMS_LOB.APPEND(
DBMS_LOB.APPEND(
DBMS_LOB.APPEND(
DBMS_LOB.APPEND(l_clob
,to_clob(crq.COl1) || ',')
,to_clob(crq.COL2) || ',')
,to_clob(crq.COL3) || ',')
,to_clob(crq.COL4) || ',' || UTL_TCP.crlf);
end loop;
for crq in (select col1,col2,col3,col4 from table where id=2 ) loop
/* Prepare Details data using Clob */
l_clob := DBMS_LOB.APPEND(
DBMS_LOB.APPEND(
DBMS_LOB.APPEND(
DBMS_LOB.APPEND(l_clob
,to_clob(crq.COl1) || ',')
,to_clob(crq.COL2) || ',')
,to_clob(crq.COL3) || ',')
,to_clob(crq.COL4) || ',' || UTL_TCP.crlf);
end loop;
dbms_output.put_line('Sending mail with attachment ');
ATTACHMENT_SEND(p_to => 'req#Email.com',
p_from => 'req#Email.com',
p_subject => ' Report ',
p_text_msg =>'Hi All',
p_attach_name =>'report_'||sysdate||'.csv',
p_attach_mime =>'text/plain',
p_attach_clob =>l_clob,
p_smtp_host =>'host.com');
dbms_lob.freetemporary(l_clob);
END RPT;
[1]: https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lob.htm#i997432

Send SQL query output as CSV attachment (pl/sql)

I have a pl/sql query and I want it's output to be sent in email in CSV format straightaway. I have no directory to first create and save a CSV file and then pick it up to send as an attachment.
Please help with your inputs as I am not able to get away.
Regards,
Sachin
Finally figured out a solution with the help of pointers received and providing the same to further help in case someone else needs in future.
My problem was that I was mostly seeing the examples where i could either save the file on a directory or pick the file from a directory to send as an attchment but I had no provision of directory and I wanted query result to be put in CSV and sent in email dynamically. So here is the complete solution.
CREATE OR REPLACE PROCEDURE SEND_CSV_ATTACHMENT AS
v_sender VARCHAR2(130);
v_recipients VARCHAR2(4000);
v_cc VARCHAR2(4000);
v_bcc VARCHAR2(2000);
v_subj VARCHAR2(200);
v_msg CLOB;
v_mime VARCHAR2(40);
v_tbl VARCHAR2(20000);
c_cr_lf CONSTANT CHAR (2) := (CHR (13) || CHR (10)); -- Carriage Return/Line Feed characters for formatting text emails
v_loop_count PLS_INTEGER := 0;
v_attachment CLOB;
v_block_qry VARCHAR2(3000);
v_block_row VARCHAR2(6000);
TYPE bl_cur IS REF CURSOR;
v_result bl_cur;
v_rowcount NUMBER;
errMsg VARCHAR2(15000);
BEGIN
v_sender := 'somesender#xyzcommunications.com';
SELECT NVL(EMAIL_LIST, 'someone#abcd.com')
FROM
(
SELECT LISTAGG(EMAIL_ID, ',') WITHIN GROUP (ORDER BY EMAIL_ID) AS EMAIL_LIST FROM RECIPEINTS_TABLE WHERE SEND_TO = 1 AND IS_ACTIVE = 1
);
SELECT NVL(EMAIL_LIST, 'someone#abcd.com')
FROM
(
SELECT LISTAGG(EMAIL_ID, ',') WITHIN GROUP (ORDER BY EMAIL_ID) AS EMAIL_LIST FROM RECIPEINTS_TABLE WHERE SEND_CC = 1 AND IS_ACTIVE = 1
);
v_bcc := 'someone#abcd.com';
-- Generate attachment - Begin
v_attachment := '"COL1", "COL2"' || CHR(13) || CHR(10);
v_block_qry := 'SELECT ''"'' || COL1 || ''", "'' || COL2 || ''"'' AS ROWTXT
FROM MY_TABLE';
OPEN v_result FOR v_block_qry;
LOOP
v_rowcount := v_result%ROWCOUNT;
FETCH v_result INTO v_block_row;
EXIT WHEN v_result%NOTFOUND;
v_attachment := v_attachment || v_block_row || chr(13) || chr(10);
END LOOP;
CLOSE v_result;
-- Generate attachment - End
v_subj:= 'MAIL_SUBJECT ' || TO_CHAR(TRUNC(SYSDATE-1), 'YYYY-MM-DD');
UTL_MAIL.send_attach_varchar2(sender => v_sender,
recipients => v_recipients,
cc => v_cc,
bcc => v_bcc,
subject => v_subj,
message => v_msg,
mime_type => 'text/html; charset=us-ascii', -- send html e-mail
attachment => v_attachment,
att_inline => FALSE,
att_filename => 'Change_Report' || TO_CHAR(TRUNC(SYSDATE-1), 'YYYY-MM-DD') || '.csv');
EXCEPTION
WHEN OTHERS THEN
errMsg := SQLERRM;
SEND_MAIL_HTML ('someone#abcd.com', NULL, NULL, errMsg, 'SEND_MAIL ERROR: ' || errMsg);
END SEND_CSV_ATTACHMENT;
You may create such a procedure :
create or replace procedure prFileSend is
v_mail_owner varchar2(100):='myname#someComp.com';
v_url varchar2(4000);
v_rep varchar2(4000);
delimiter varchar2(1) := chr(38);
begin
for c in ( select * from myTable )
loop
begin
v_url := 'http://www.mycompany.com/einfo/default.aspx?email='||c.email || delimiter || 'p1=' || c.col1 || delimiter ||'p2='||c.col2;
v_rep := utl_http.request(utl_url.escape(v_url, false,'ISO-8859-9'));
end;
end loop;
exception
when others then
prErrorMsgSend(v_mail_owner,'Error : ' || sqlerrm); -- a function like this one which sends an error message back to you.
end;
and create a scheduler job
begin
dbms_scheduler.create_job (
job_name => 'jbFileSend ',
job_type => 'STORED_PROCEDURE',
job_action => 'prFileSend',
start_date => '22-jan-2018 09:00:00 am',
repeat_interval => 'FREQ=DAILY; INTERVAL=1',
comments => 'Sending Every day'
enabled => true);
end;
working every day as an example.

How to sent email in Oracle PL/SQL package to multiple receivers?

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

Debugging with Oracle's utl_smtp

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?

Resources