Send Email Using PLSQL - oracle

I want to Send Email via gmail or yahoo host using PL_SQL, i searched in google and find SMT.Mail package but it did't work for me, Please any one can guide me how will i achieve this goal ?
CREATE OR REPLACE PROCEDURE
send_mail (sender IN VARCHAR2,
recipient IN VARCHAR2,
message IN VARCHAR2,
nStatus OUT NUMBER)
IS
mailhost VARCHAR2(30) := 'smtp.gmail.com '; -- host mail addr
mail_conn utl_smtp.connection;
BEGIN
nStatus := 0;
mail_conn := utl_smtp.open_connection(mailhost, 25);
utl_smtp.helo(mail_conn, mailhost);
utl_smtp.mail(mail_conn, sender);
utl_smtp.rcpt(mail_conn, recipient);
utl_smtp.data(mail_conn, message);
utl_smtp.quit(mail_conn);
EXCEPTION
WHEN OTHERS THEN
nStatus := SQLCODE;
END send_mail;
when i test this procedure I get: ORA-29278: SMTP transient error: 421 Service not available

You missed your authentication at the server, see AUTH Function and Procedure
However, I do not know whether gmail allows to use port 25 which is not secure by default.

My experience has been that the utl_mail package is much easier to use. Here is a silly example:
BEGIN
UTL_MAIL.send (sender => 'bighearted#somewhere.com'
, recipients => 'receiver#footballreceiver.com'
, subject => 'Goofy Messages'
, MESSAGE => 'Please don''t send any more goofy messages'
, mime_type => 'text/html; charset=us-ascii');
END;
You must set the system parameter smtp_out_server to the name of your email server.
With Oracle 11G R2 and beyond, you must set up the proper access control list (ACL) for this to work. This is the code that I use to do so.
DECLARE
-- ACL name to be used for email access reuse the same value for all
-- future calls
l_acl VARCHAR2 (30) := 'utl_smtp.xml';
-- Oracle user to be given permission to send email
l_principal VARCHAR2 (30) := 'CEAADMIN';
-- Name of email server
g_mailhost VARCHAR2 (60) := 'mail.yourserver.com';
l_cnt INTEGER;
PROCEDURE validate_smtp_server
AS
l_value v$parameter.VALUE%TYPE;
l_parameter v$parameter.name%TYPE := 'smtp_out_server';
BEGIN
SELECT VALUE
INTO l_value
FROM v$parameter
WHERE name = l_parameter;
IF l_value IS NULL
THEN
raise_application_error (
-20001
, 'Oracle parameter '
|| l_parameter
|| ' has not been set'
|| UTL_TCP.crlf
|| 'it s/b mail.yourserver.com'
);
END IF;
DBMS_OUTPUT.put_line ('parameter ' || l_parameter || ' value is ' || l_value);
END validate_smtp_server;
PROCEDURE create_if_needed (p_acl IN VARCHAR2)
AS
l_cnt INTEGER;
BEGIN
SELECT COUNT (*) c
INTO l_cnt
FROM dba_network_acls a
WHERE SUBSTR (acl, INSTR (acl, '/', -1) + 1) = p_acl;
IF l_cnt = 0
THEN
DBMS_OUTPUT.put_line ('creating acl ' || p_acl);
DBMS_NETWORK_ACL_ADMIN.create_acl (
acl => p_acl
, description => 'Allow use of utl_smtp'
, principal => l_principal
, is_grant => TRUE
, privilege => 'connect'
);
DBMS_NETWORK_ACL_ADMIN.assign_acl (acl => p_acl, HOST => g_mailhost);
COMMIT;
ELSE
DBMS_OUTPUT.put_line (p_acl || ' acl already exists');
END IF;
END create_if_needed;
PROCEDURE add_if_needed (
p_principal IN VARCHAR2
, p_acl IN VARCHAR2
)
AS
l_cnt INTEGER;
BEGIN
SELECT COUNT (*) c
INTO l_cnt
FROM dba_network_acl_privileges
WHERE SUBSTR (acl, INSTR (acl, '/', -1) + 1) = p_acl
AND principal = p_principal;
IF l_cnt = 0
THEN
DBMS_NETWORK_ACL_ADMIN.add_privilege (
acl => 'utl_smtp.xml'
, principal => p_principal
, is_grant => TRUE
, privilege => 'connect'
);
COMMIT;
DBMS_OUTPUT.put_line ('access to ' || p_acl || ' added for ' || p_principal);
ELSE
DBMS_OUTPUT.put_line (p_principal || ' already has access to ' || p_acl);
END IF;
END add_if_needed;
BEGIN
EXECUTE IMMEDIATE 'grant execute on utl_mail to ' || l_principal;
create_if_needed (p_acl => l_acl);
add_if_needed (p_principal => l_principal, p_acl => l_acl);
DBMS_OUTPUT.put_line ('Verification SQL:');
DBMS_OUTPUT.put_line (' SELECT * FROM dba_network_acls;');
DBMS_OUTPUT.put_line (' SELECT * FROM dba_network_acl_privileges;');
COMMIT;
validate_smtp_server;
END;

Related

How can I increment a date by a few months?

This is my code that I have written:
create or replace procedure UPDATING_LOCATION(P_SHIPMENT_NUM IN NUMBER,
P_SHIPMENT_DESTINATION IN VARCHAR2,
P_ERROR OUT VARCHAR2)
is
begin
UPDATE SHIPMENT S
SET S.SHIPMENT_DESTINATION = P_SHIPMENT_DESTINATION
WHERE S.SHIPMENT_NUM = P_SHIPMENT_NUM;
IF SHIPMENT_NUM = '11'
THEN
TO_DATE('06-05-2020') + 1
ELSE
P_MESSAGE := 'The shipment will be on time, which is ' || P_SHIPMENT_DATE;
EXCEPTION
WHEN OTHERS THEN
P_ERROR := 'UPDATE WAS FAILED FOR SHIPMENT' || P_SHIPMENT_NUM || '- SQL ERROR: '||SQLERRM;
end UPDATING_LOCATION;
You are not using P_SHIPMENT_DATE anywhere in your procedure's parameter list. I think that should be your OUT parameter in your procedure. -
create or replace procedure UPDATING_LOCATION(P_SHIPMENT_NUM IN NUMBER,
P_SHIPMENT_DESTINATION IN VARCHAR2,
P_SHIPMENT_DATE OUT DATE,
P_ERROR OUT VARCHAR2)
is
begin
UPDATE SHIPMENT S
SET S.SHIPMENT_DESTINATION = P_SHIPMENT_DESTINATION
WHERE S.SHIPMENT_NUM = P_SHIPMENT_NUM;
IF SHIPMENT_NUM = '11'
THEN
P_SHIPMENT_DATE := TO_DATE('06-05-2020', 'DD-MM-YYYY') + 1; -- You have to pick this date from somewhere.
P_MESSAGE := 'The shipment will be delayed by 1 day.';
ELSE
P_SHIPMENT_DATE := TO_DATE('06-05-2020', 'DD-MM-YYYY'); -- You have to pick this date from somewhere.
P_MESSAGE := 'The shipment will be on time, which is ' || TO_DATE('05-05-2020', 'DD-MM-YYYY');
END IF;
EXCEPTION
WHEN OTHERS THEN
P_ERROR := 'UPDATE WAS FAILED FOR SHIPMENT' || P_SHIPMENT_NUM || '- SQL ERROR: '||SQLERRM;
end UPDATING_LOCATION;

Retrieving password from an oracle package

I have the following code, my question is how can I be sure the encryption key in l_key has not been changed?
create or replace PACKAGE BODY "ENCRYPT_DECRYPT_PASSWORD"
AS
l_key RAW(128) := utl_raw.cast_to_raw('secret');
------------------------------------------------------------------------
FUNCTION encrypt_val( p_val IN VARCHAR2 ) RETURN VARCHAR2
IS
l_encrypted RAW(2048);
l_val RAW(2048) := utl_raw.cast_to_raw(p_val);
BEGIN
l_encrypted := dbms_crypto.encrypt
( src => l_val,
typ => dbms_crypto.des_cbc_pkcs5,
key => l_key );
return utl_raw.cast_to_varchar2(l_encrypted);
END encrypt_val;
-----------------
-----------------
-----------------
FUNCTION decrypt_val( p_val IN varchar2 ) RETURN VARCHAR2
IS
l_decrypted RAW(2048);
l_val RAW(2048) := utl_raw.cast_to_raw(p_val);
BEGIN
l_decrypted := dbms_crypto.decrypt
( src => l_val,
typ => dbms_crypto.des_cbc_pkcs5,
key => l_key );
return utl_raw.cast_to_varchar2(l_decrypted);
END decrypt_val;
-----------------
-----------------
-----------------
PROCEDURE encrypt_table_passwords(table_name IN varchar2,
column_name IN varchar2,
table_id IN varchar2) IS
BEGIN
EXECUTE IMMEDIATE
'begin
for c1 in (select * from ' || table_name ||') loop
update ' || table_name || ' set ' || column_name || ' = ENCRYPT_DECRYPT_PASSWORD.encrypt_val(c1.' || column_name || ') where ' || table_id || ' = c1.'||table_id||' and ' || column_name ||
' is not null; end loop; end;';
END encrypt_table_passwords;
-----------------
-----------------
-----------------
FUNCTION get_decrypted_password( table_name IN varchar2,column_name IN varchar2,table_id IN varchar2,table_id_val IN varchar2 ) RETURN VARCHAR2
IS
encrypted_pas varchar2(100);
decrypted_pas varchar2(100);
BEGIN
EXECUTE IMMEDIATE 'select ' || column_name || ' from ' || table_name || ' where ' || table_id || ' = ' || table_id_val
INTO encrypted_pas;
Select decrypt_val(encrypted_pas) into decrypted_pas from dual;
--return decrypt_val(encrypted_pas);
return decrypted_pas;
END get_decrypted_password;
END encrypt_decrypt_password;
I tried the following which I found on the web, but it appears it doesn't work for my oracle version:
DECLARE
v_x RAW(128);
BEGIN
SELECT ENCRYPT_DECRYPT_PASSWORD.l_key x
INTO v_x
FROM DUAL;
DBMS_OUTPUT.put_line (v_x);
END;
/
I get "component 'L_KEY' must be declared" and "invalid identifier".
The reason I want to investigate is that I had decryption errors which went away after I changed the password column from varchar(99) to nvarchar2(100) and after I regenerated the encrypted password.
I get "component 'L_KEY' must be declared" and "invalid identifier".
L_KEY is declared in your package BODY. That means its scope is private, restricted to the code of the body. Only things declared in the package SPEC are public and can be accessed outside the package scope.
If you need to check its actual value you need to extend your package with a function which returns L_KEY. You probably don't want to expose it in Production so be careful. You may wish to consider using conditional compilation just to be sure nothing accidentally leaks.

sql statment in email messge utl_mail.send

I am sending emails true oracle apex which works fine. what i need however is to have an sql count statement in the message of the email.
what i have is
begin
utl_mail.send(sender => 'a#test.com',
recipients =>'a#test.com',
subject => 'FileRequest',
message => 'select count(filenumber) where status is assigned' files from registry '||:p5_filenumber ||''||' ' ||:p5_filename || ' has now been assigned to the ' || :p5_department || '');
end;
which obviously is not working
what i will like to see is
begin
utl_mail.send(sender => 'a#test.com',
recipients =>'a#test.com',
subject => 'FileRequest',
message => 5 files from registry '||:p5_filenumber ||''||' ' ||:p5_filename || ' has now been assigned to the ' || :p5_department || '');
end;
Compute the value BEFORE sending the mail. Something like this:
declare
l_cnt number;
l_msg varchar2(200);
begin
-- select number you're interested in
select count(*)
into l_cnt
from some_table
where some_conditions;
-- compose the message
l_msg := l_cnt ||' files from registry ...';
-- send mail
utl_mail.send(sender => 'a#test.com',
recipients => 'a#test.com',
subject => 'FileRequest',
message => l_msg);
end;

ORA 6502 character string buffer too small

I am a newbie in PL/SQL
I have an PL SQL. And im getting an error shown in the title. ORA 6502 character string buffer too small.
create or replace
PROCEDURE MailSender IS
tmpVar VARCHAR2(2048);
BEGIN
FOR cur_rec IN
(SELECT * FROM dom_email1 where rownum <= 50 and eto is not null ORDER BY eid asc)
LOOP
tmpVar := ltrim(cur_rec.ETO, ' ; ');
tmpVar := rtrim(tmpVar, '; ');
tmpVar := rtrim(tmpVar, ' ');
DOMSYS_EMAIL.SEND_EMAIL(msg_from => 'noreply#gmail.com'
, msg_tos => tmpVar
, msg_subject => cur_rec.SUBJ
, msg_text => cur_rec.MSG
, mailhost => '10.63.17.38');
UPDATE DOM_EMAIL1 SET eid='1' WHERE eid= cur_rec.EID;
END LOOP;
DELETE FROM DOM_EMAIL1 WHERE eid='1';
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
RAISE;
END MailSender;
Issue is due to the size is not satisfied for any of the below variables/cursor value you are passing in your procedure
tmpVar
cur_rec.SUBJ
cur_rec.MSG
Add the below line in your procedure before send mail (DOMSYS_EMAIL.SEND_EMAIL) to validate the size of each value you passed and check whether it satisfies the parameter size limit of the DOMSYS_EMAIL.SEND_EMAIL procedure
dbms_output.put_line('tmpVar :' || length(tmpVar) ||' - '|| 'cur_rec.SUBJ :'|| length(cur_rec.SUBJ) ||' - '|| 'cur_rec.MSG :' || length(cur_rec.MSG));
For varchar2,you can update the size limit if required after validation

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.

Resources