Sending email from oracle apex UTL_SMTP with smtp server credentials - oracle

Is there a way to send email using UTL_SMTP method by providing smtp server credentials which is not localhost.
i.e. SMTP service is running somewhere else but is not https secures, i want to use utl_smtp package but i dont see any function parameter that take smtp server id or password in it

If you want to use UTL_SMTP, you provide the SMTP host when you call utl_smtp.open_connection.
Example code (taken from Oracle docs);
DECLARE
c UTL_SMTP.CONNECTION;
PROCEDURE send_header(name IN VARCHAR2, header IN VARCHAR2) AS
BEGIN
UTL_SMTP.WRITE_DATA(c, name || ': ' || header || UTL_TCP.CRLF);
END;
BEGIN
c := UTL_SMTP.OPEN_CONNECTION('smtp-server.acme.com');
UTL_SMTP.HELO(c, 'foo.com');
UTL_SMTP.MAIL(c, 'sender#foo.com');
UTL_SMTP.RCPT(c, 'recipient#foo.com');
UTL_SMTP.OPEN_DATA(c);
send_header('From', '"Sender" <sender#foo.com>');
send_header('To', '"Recipient" <recipient#foo.com>');
send_header('Subject', 'Hello');
UTL_SMTP.WRITE_DATA(c, UTL_TCP.CRLF || 'Hello, world!');
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 raises an
-- exception that we can ignore.
END;
raise_application_error(-20000,
'Failed to send mail due to the following error: ' || sqlerrm);
END;
If your smtp server accepts PLAIN, LOGIN or CRAM-MD5 authentication, you need to call utl_smtp.auth as well, e.g.:
DECLARE
c utl_smtp.connection;
BEGIN
c := utl_smtp.open_connection(
host => 'smtp.example.com',
port => 25,
wallet_path => 'file:/oracle/wallets/smtp_wallet',
wallet_password => 'password',
secure_connection_before_smtp => FALSE);
UTL_SMTP.STARTTLS(c);
UTL_SMTP.AUTH(
c => c,
username => 'scott',
password => 'password'
schemes => utl_smtp.all_schemes);
END;
That said, if you're using APEX you may want to consider using APEX_MAIL instead. You configure the host in the APEX instance settings.
Documentation:
UTL_SMTP: https://docs.oracle.com/cd/E11882_01/appdev.112/e40758/u_smtp.htm#ARPLS074
APEX_MAIL: https://docs.oracle.com/database/apex-5.1/AEAPI/APEX_MAIL.htm

Related

Oracle Cloud > utl_http fails with a ORA-29273: HTTP request failed ORA-29024: Certificate validation failure ORA-06512

I am working with a temporary Oracle Cloud account. What I thought was identical functionality works on the Apex Web Service, but not with UTL_HTTP. So this snippet works and returns the issue we are looking for.
DECLARE
L_json_response varchar2(32767);
BEGIN
apex_web_service.g_request_headers(1).name := 'Content-Type';
apex_web_service.g_request_headers(1).Value := 'application/json';
L_json_response := apex_web_service.make_rest_request ( p_url =>
'https://mycompany.atlassian.net/rest/api/3/issue/BLABLA-23862', p_http_method => 'GET',
p_username => 'My.Username#mycompany', p_password => 'osBJWHhPasdffNVOQ5AA11D5'); -- Password is my Jira API Token
EXCEPTION WHEN OTHERS THEN raise_application_error(-20001,'An error was encountered -
'||SQLCODE||' -ERROR- '||SQLERRM);
END;
I can't use the Apex web service in the end product, and we need to use UTL_HTTP. This snippet should from my understanding do the same:
DECLARE
req utl_http.req;
res utl_http.resp;
url varchar2(4000) := 'https://mycompany.atlassian.net/rest/api/3/issue/BLABLA-23862';
buffer varchar2(4000);
BEGIN
req := utl_http.begin_request(url, 'GET');
utl_http.set_header(req, 'Content-Type', 'application/json');
utl_http.set_header(req, 'Authorization', 'Basic ' || utl_encode.base64_encode('my.msername#mycompany:osBJWHhPasdffNVOQ5AA11D5'));
res := utl_http.get_response(req);
utl_http.read_text(res, buffer);
END;
But returns:
ORA-29273: HTTP request failed
ORA-29024: Certificate validation failure
ORA-06512: at "SYS.UTL_HTTP", line 639
ORA-06512: at "SYS.UTL_HTTP", line 1415 ORA-06512...
The key is the UTL_HTTP.SET_WALLET('');. You need to set the wallet(with empty string parameter) before initial http request.
The following code snippet tested with in Oracle Cloud ATP (Autonomous Transition Processing) database:
Setup network ACL
BEGIN
DBMS_NETWORK_ACL_ADMIN.CREATE_ACL(acl => 'my_acl.xml',
description => 'ACL for http request.',
principal => 'MY_USER',
is_grant => true,
privilege => 'connect');
DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE(acl => 'my_acl.xml',
principal => 'MY_USER',
is_grant => true,
privilege => 'resolve');
DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL(acl => 'my_acl.xml', host => 'www.oracle.com');
END;
Test https request
DECLARE
l_text VARCHAR2(32767);
BEGIN
UTL_HTTP.SET_WALLET('');
l_text := UTL_HTTP.REQUEST('https://www.oracle.com/index.html');
dbms_output.put_line(l_text);
END;
See official document and example (on the bottom of page):
In a way #OldProgrammer answered this the comment from 29-12-2020m but I don't know how to attribute the answer.
I needed to either couple the UTL_HTTP code to default wallet or create a new one. APEX_WEB_SERVICE probably does this implicitly while UTL_HTTP has to have it spelled out.
My question was for Oracle Cloud and I couldn't make the toolchain from OldProgrammers link work in Oracle Cloud. Rolled out a VM and it works.

Consuming rest API from Oracle PLSQL is returning ??? for arabic characters

I am trying to call a restful API from my Oracle procedure.
First, the API method is of type get and not post, so parameters are sent through header. the main purpose of the API is to send the received message as SMS to some providers and sometimes they are in Arabic format; We realized that Arabic received SMS are incomprehensible;
So I created a test procedure that takes a message and sends it to a test API method that returns the same message as response.
The API call succeeded but the response, only when arabic format is used, looks like ����. What should be added to my procedure so messages can be readable? I have tried to use escape for the message and to set the header format as you can see in below template, but unfortunately nothing succeeded:
PROCEDURE TEST(lang VARCHAR2,
message VARCHAR2,
P_RESPONSE OUT VARCHAR2) AS
v_request UTL_HTTP.req;
v_response UTL_HTTP.resp;
v_text VARCHAR2(1024);
v_url VARCHAR2(1024);
v_message VARCHAR2(1024);
l_webservice_link VARCHAR2(128);
BEGIN
BEGIN
P_RESPONSE := '';
v_message := utl_url.escape(message);
--v_message :=utl_url.escape(message,false,'UTF-8');
--v_message :=utl_url.escape(message,false,'windows-1256');
--v_message :=utl_url.escape(message,false,'AL32UTF8');
--v_message :=utl_url.escape(message,false,'AR8MSWIN1256');
l_webservice_link := GET_PARAM('REST_API_URL');
v_url := l_webservice_link ||
'Mytest?strMessage=' || v_message||
'&strLang=' || lang;
v_request := UTL_HTTP.begin_request(v_url);
--UTL_HTTP.set_header(v_request, 'Content-Type', 'charset=UTF-8');
--UTL_HTTP.set_header(v_request, 'Content-Type', 'windows-1256');
v_response := UTL_HTTP.get_response(v_request);
LOOP
BEGIN
UTL_HTTP.read_text(v_response, v_text);
DBMS_OUTPUT.put_line(v_text);
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
NULL;
END;
EXIT WHEN v_text IS NULL;
END LOOP;
UTL_HTTP.end_response(v_response);
IF v_response.status_code <> 200 THEN
P_RESPONSE := v_response.reason_phrase;
END IF;
EXCEPTION
WHEN OTHERS THEN
P_RESPONSE := 'An error has occured: ' || SQLERRM;
END;
END TEST;
Any help is more than appreciated.
Try to insert this code:
Charset VARCHAR2(20);
BEGIN
SELECT UTL_I18N.MAP_CHARSET(VALUE)
INTO Charset
FROM nls_database_parameters
WHERE parameter = 'NLS_CHARACTERSET';
UTL_HTTP.set_header(v_request, 'Content-Type', 'text/html; charset='||Charset);
I am not familiar with REST, I don't know wether text/html; is required and correct.
Update
I just see your database character set it AR8ASMO8X which does not have any IANA name (at least not according to Oracle UTL_I18N.MAP_CHARSET)
In this case try
UTL_HTTP.set_header(v_request, 'Content-Type', 'text/html; charset=UTF-8');
UTL_HTTP.begin_request(CONVERT(v_url,'AL32UTF8'));
Most likely the server returns response in UTF-8 - would be the most common one, otherwise check the header of the response.
Then try this:
UTL_HTTP.SET_BODY_CHARSET(v_response, 'AL32UTF8');
Apart from all above you may have also a display issue, i.e. inside Oracle everything would be fine, just your client is not able to display the characters properly, see OdbcConnection returning Chinese Characters as "?"

Oracle Invoke REST Service from pl/sql

I need help with this code, I am getting an error saying following.
ORA-29273: HTTP request failed
ORA-12541: TNS:no listener
ORA-06512: at "SYS.UTL_HTTP", line 368
ORA-06512: at "SYS.UTL_HTTP", line 1118
ORA-06512: at line 5
29273. 00000 - "HTTP request failed"
*Cause: The UTL_HTTP package failed to execute the HTTP request.
*Action: Use get_detailed_sqlerrm to check the detailed error message.
Fix the error and retry the HTTP request.
I contacted network team and they see bidirectional traffic on that port being done, so I am not sure what else is/could be wrong? any ideas?
create or replace
procedure Test_Rest_Call3
is
req utl_http.req;
res utl_http.resp;
url varchar2(4000) := 'http://ipaddresshere:9099/api/batchProcess/1';
name varchar2(4000);
buffer varchar2(4000);
content varchar2(4000) := '';
begin
req := utl_http.begin_request(url, 'DELETE',' HTTP/1.1');
utl_http.set_header(req, 'user-agent', 'mozilla/4.0');
utl_http.set_header(req, 'content-type', 'application/json');
utl_http.set_header(req, 'Content-Length', length(content));
utl_http.write_text(req, content);
res := utl_http.get_response(req);
-- process the response from the HTTP call
begin
loop
utl_http.read_line(res, buffer);
dbms_output.put_line(buffer);
end loop;
utl_http.end_response(res);
exception
when utl_http.end_of_body
then
utl_http.end_response(res);
end;
end Test_Rest_Call3;
Have you checked if DBA granted to execute utl_http?
Just try to run it to be sure:
select utl_http.request('http://ipaddresshere:9099/api/batchProcess/1') from dual;
If you get error then ask DBA to give the permission accordingly:
grant execute on utl_http to your_oracle_user_name
grant execute on dbms_lock to user_name
BEGIN
DBMS_NETWORK_ACL_ADMIN.create_acl (
acl => 'local_sx_acl_file.xml',
description => 'A test of the ACL functionality',
principal => 'put your user_name',
is_grant => TRUE,
privilege => 'connect',
start_date => SYSTIMESTAMP,
end_date => NULL);
end;
begin
DBMS_NETWORK_ACL_ADMIN.assign_acl (
acl => 'local_sx_acl_file.xml',
host => 'localhost',
lower_port => 9002,
upper_port => NULL);
end;
I hope it will help.
Cheers,
Morteza Fakoorrad

ORA-29278: SMTP transient error: Service not available when running UTL_MAIL

We are planning to install the UTL_MAIL Package and we're currently testing the installation steps in our Development Environment.
After sucessfully installing the UTL_MAIL Package Scripts and creating the sufficient PUBLIC Synonyms and Grants,
we are getting the error ORA-29278 when running the test Anonymous Block below:
BEGIN
UTL_MAIL.SEND(sender => 'xxx#oracle.com'
, recipients => 'Migs.Isip.23#gmail.com'
, subject => 'Testmail'
, message => 'Hello');
END;
Full Details of the error Message:
ORA-29278: SMTP transient error: 421 4.3.2 Service not available
ORA-06512: at "SYS.UTL_MAIL", line 662
ORA-06512: at "SYS.UTL_MAIL", line 679
ORA-06512: at line 3
29278. 00000 - "SMTP transient error: %s"
*Cause: A SMTP transient error occurred.
*Action: Correct the error and retry the SMTP operation.
As per research from related links (Send Email Using PLSQL),
i may need to setup the proper access control list (ACL) for this to work. However, upon executing the script below, i'm still getting the same error.
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) := 'APPS';
-- Name of email server
g_mailhost VARCHAR2 (60) := 'smtprelay.xxxxx.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 smtprelay.alorica.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;
What other steps can i take or what other instructions do i need to provide to the DBAs for this?
Oracle Database Version:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
"CORE 11.2.0.4.0 Production"
TNS for Solaris: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production
Thank you very much.
I was able to resolve this by contacting our System Administrator and asking for the details of the Mail Server.
Turns out, if we'll only be sending the email internally, we are advised to use a different server mail.xxx.xxx.xxxx since its not going to be blocked by the firewall.
On the other hand, if we'll be sending email externally, another server is involved smtprelay.xxxxx.com
and this involves an extra step of Whitelisting the External Servers to be sent to.
As i checked in V$PARAMETER, we were using the smtprelay.xxxxx.com server and decided to try the other server mail.xxx.xxx.xxxx.
I issued the Alter command as below:
alter system set smtp_out_server = 'mail.xxx.xxx.xxxx';
and ran the anonymous block and was able to recieve the email successfully.
BEGIN
UTL_MAIL.SEND(sender => 'xxx#oracle.com'
, recipients => 'Migs.Isip.23#gmail.com'
, subject => 'Testmail'
, message => 'Hello');
END;
I came up with SMTP email challenges for 19c database. I was able to solve it. Below is the complete solution:-
Give grant to corresponding schema name for utl_tcp,utl_smtp and utl_http.
grant execute on utl_tcp to schemaname;
grant execute on utl_smtp to schemaname;
grant execute on utl_http to schemaname;
CREATE_ACL using DBMS_NETWORK_ACL_ADMIN sys package:-
BEGIN
DBMS_NETWORK_ACL_ADMIN.CREATE_ACL (
acl => '/sys/acls/utl_http.xml',
description => 'Allowing SMTP Connection',
principal => 'SCHEMANAME',
is_grant => TRUE,
privilege => 'connect',
start_date => SYSTIMESTAMP,
end_date => NULL);
COMMIT;
END;
/
ADD_PRIVILEGE to schema using DBMS_NETWORK_ACL_ADMIN package:-
BEGIN
DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE(
acl => '/sys/acls/utl_http.xml',
principal => 'SCHEMANAME',
is_grant => true,
privilege => 'resolve');
COMMIT;
END;
/
ASSIGN_ACL to mail server using DBMS_NETWORK_ACL_ADMIN package:-
BEGIN
DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL (
acl => '/sys/acls/utl_http.xml',
host => 'mailhostname');
COMMIT;
END;
/

ORA-24247 when using utl_http.read_text. Only appear after a delay of about 60 seconds

I am using the following code to read the response of an http request:
FUNCTION readClob (ww_reponse IN OUT NOCOPY UTL_HTTP.resp)
RETURN CLOB
IS
l_clob CLOB;
l_text VARCHAR2 (32767);
BEGIN
DBMS_LOB.createtemporary (l_clob, FALSE);
BEGIN
LOOP
=> UTL_HTTP.read_text (ww_reponse, l_text, 32767);
DBMS_LOB.writeappend (l_clob, LENGTH (l_text), l_text);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body
THEN
UTL_HTTP.end_response (ww_reponse);
END;
This is working fine for small request, but for longer request (about >60 sec), I get the ORA-24247 error (network access denied by access control list (ACL)) on the read_text instruction, but to my surprise not on the first call but on some random next iteration, about 60 seconds later. My ACL are ok, otherwise nothing would ever work.
I also have set a larger timeout. Obviously the error is not the 'real' error. Anyone have the same problem and solution ? Thank you.
Environment:
Oracle Database 11g Enterprise Edition on Windows Server 2008(64bits)
I finally went to Oracle support and found this:
Drop and recreate the ACL as shown below
BEGIN
DBMS_NETWORK_ACL_ADMIN.CREATE_ACL(
acl => 'wallet-acl.xml',
description => 'Wallet ACL',
principal => 'APPS',
is_grant => TRUE,
privilege => 'use-client-certificates');
DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE(
acl => 'wallet-acl.xml',
principal => 'APPS',
is_grant => TRUE,
privilege => 'use-passwords');
DBMS_NETWORK_ACL_ADMIN.ASSIGN_WALLET_ACL(
acl => 'wallet-acl.xml',
wallet_path => 'file:');
END;
/
BUT then the message I get is corrupted (misses some characters).
I have added the sleep instruction in the loop and now everything is fine. Not a real solution but a workaround at best.
FUNCTION readClob (ww_reponse IN OUT NOCOPY UTL_HTTP.resp)
RETURN CLOB
IS
l_clob CLOB;
l_text VARCHAR2 (32767);
BEGIN
DBMS_LOB.createtemporary (l_clob, FALSE);
BEGIN
LOOP
UTL_HTTP.read_text (ww_reponse, l_text, 32767);
=> **dbms_lock.sleep(0.1);**
DBMS_LOB.writeappend (l_clob, LENGTH (l_text), l_text);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body
THEN
UTL_HTTP.end_response (ww_reponse);
END;

Resources