How to send a message to multiple recipients - oracle

I've tried to send a message to a single recipient by using
UTL_SMTP.rcpt(l_mail_conn, p_to);
where p_to can contain only a email address. In order to send a message to multiple recipients, the only possibility (as far as I know) is to call this routine multiple times, but is there another way to send the same message to multiple recipients all at once so as to allow every recipient to see the others instead of having multiple emails ?
Oracle version 10g
Thanks
Mark

As far as I can tell, it is one-by-one. However, you don't have to send "n" mails, just call UTL_SMPT.RCPT as many times as needed.
For example, in my procedure, I'm passing separate parameters for To, Cc and Bcc and then concatenate them into a single local variable (they should be split by semi-colon):
l_recipients :=
par_to
|| CASE WHEN par_cc IS NOT NULL THEN ';' || par_cc END
|| CASE WHEN par_bcc IS NOT NULL THEN ';' || par_bcc END;
and then - in a loop - calling the RCPT procedure for each recipient:
FOR cur_r IN ( SELECT REGEXP_SUBSTR (l_recipients,
'[^;]+',
1,
LEVEL) recipient
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT (l_recipients, ';') + 1)
LOOP
UTL_SMTP.rcpt (l_smtp_connection, cur_r.recipient);
END LOOP;
(Note that this is 11g example; 10g - you use - doesn't have REGEXP_COUNT function, but that's not the main issue here).
Later, I'm just saying who is who as
-- From / To / Cc / Bcc
UTL_SMTP.write_data (l_smtp_connection,
'From: ' || par_sender || UTL_TCP.crlf);
UTL_SMTP.write_data (l_smtp_connection,
'To: ' || par_to || UTL_TCP.crlf);
UTL_SMTP.write_data (l_smtp_connection,
'Cc: ' || par_cc || UTL_TCP.crlf);
UTL_SMTP.write_data (l_smtp_connection,
'BCc: ' || par_bcc || UTL_TCP.crlf);

Related

How to send multiple people the mail using UTL_SMTP

I have been trying so hard but not able to implement
Any solution is great appreciated !!!
I am using APEX 20.x
I Built one form which as following content in it : textfiled , textarea , choosfile , button
FROM EMAIL : TextFiled1
SUBJECT : TextFiled2
NOTES : TextArea1
ATTACHMENT : choosefile1
SUBMIT
Database records in table : EMPLOYEE
ID,NAME,AGE,EMAIL,STATUS
11,ALICE,12,abc#protonmail.com,Y
32,BOB,32,acd#protonmail.com,N
45,RAY,22,xyz#protonmail.com,Y
36,TIN,24,tin#protonmail.com,Y
My code :
CREATE OR REPLACE PROCEDURE send_mail (p_to VARCHAR2,
p_from IN VARCHAR2,
p_subject IN VARCHAR2,
p_message IN VARCHAR2,
p_attachment IN BLOB,
p_notes IN CLOB,
p_smtp_host IN VARCHAR2,
p_smtp_port IN NUMBER DEFAULT 25)
AS
l_mail_conn UTL_SMTP.connection;
BEGIN
l_mail_conn := UTL_SMTP.open_connection(p_smtp_host, p_smtp_port);
UTL_SMTP.helo(l_mail_conn, p_smtp_host);
UTL_SMTP.mail(l_mail_conn, p_from);
UTL_SMTP.rcpt(l_mail_conn, p_to);
UTL_SMTP.open_data(l_mail_conn);
UTL_SMTP.write_data(l_mail_conn, 'Date: ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') || UTL_TCP.crlf);
UTL_SMTP.write_data(l_mail_conn, 'To: ' || p_to || UTL_TCP.crlf);
UTL_SMTP.write_data(l_mail_conn, 'From: ' || p_from || UTL_TCP.crlf);
UTL_SMTP.write_data(l_mail_conn, 'Subject: ' || p_subject || UTL_TCP.crlf);
UTL_SMTP.write_data(l_mail_conn, 'Reply-To: ' || p_from || UTL_TCP.crlf || UTL_TCP.crlf);
UTL_SMTP.write_data(l_mail_conn, p_message || UTL_TCP.crlf || UTL_TCP.crlf);
UTL_SMTP.close_data(l_mail_conn);
UTL_SMTP.quit(l_mail_conn);
END;
/
What I am trying to implement :
I need to iterate through my table EMPLOYEE and read one by one email id's and send email to them.
Need to write some for loop to iterate the employee table ? and shoot mail to them with details entered in components along with that if attachment if choosen [optional] : SUBJECT , NOTES , ATTACHMENT
I have 700+ email ids
Need to send all of them same details over their mail one by one reading from table EMPLOYEE

Send Email procedure to multiple receivers

May I know my procedure any problem? I try to run the procedure and it works only for "CC" list receivers but "TO" list receivers cannot to receive the email. Any body can help me out will be great appreciate! Please provide me modified code.
create or replace PROCEDURE check_ap_reject_list (errbuf VARCHAR2, retcode NUMBER) IS
CURSOR c1 IS
SELECT distinct b.NAME AP_ORG, a.ORG_ID ORG_ID, a.source source,
a.INVOICE_ID INVOICE_ID,reject_lookup_code Reject_Reason,
a.INVOICE_NUM Invoice_Num, a.INVOICE_DATE Invoice_Date, a.GL_DATE GL_Date,
a.VENDOR_NAME Vendor_Name, a.VENDOR_SITE_CODE Vendor_Site_Code,
a.INVOICE_AMOUNT INVOICE_AMOUNT, a.INVOICE_CURRENCY_CODE INVOICE_CURRENCY_CODE, a.EXCHANGE_RATE EXCHANGE_RATE
FROM apps.ap_interface_rejections,
apps.ap_invoices_interface a,
apps.hr_all_organization_units b,
apps.ap_invoice_lines_interface d
WHERE (((parent_id = a.invoice_id AND parent_table = 'AP_INVOICES_INTERFACE') OR (parent_id = d.invoice_line_id AND parent_table = 'AP_INVOICE_LINES_INTERFACE')))
AND a.INVOICE_ID = d.INVOICE_ID
AND a.ORG_ID = b.ORGANIZATION_ID
AND a.source = 'RMS'
;
r1 c1%rowtype;
c utl_smtp.connection;
v_instance varchar2(9);
x_email_to varchar2(4000);
x_email_cc varchar2(4000);
v_temp_str varchar2(4000);
v_temp number;
v_r1_row_num number;
v_check number;
PROCEDURE send_data(p_data in varchar2) as
BEGIN
utl_smtp.write_raw_data(c, utl_raw.cast_to_raw(p_data || utl_tcp.crlf));
--fnd_file.put_line(fnd_file.output, p_data);
END;
BEGIN
SELECT count(distinct a.INVOICE_NUM)
into v_check
FROM apps.ap_interface_rejections, apps.ap_invoices_interface a, apps.hr_all_organization_units b, apps.ap_invoice_lines_interface d
WHERE (((parent_id = a.invoice_id AND parent_table = 'AP_INVOICES_INTERFACE') OR (parent_id = d.invoice_line_id AND parent_table = 'AP_INVOICE_LINES_INTERFACE')))
AND a.INVOICE_ID = d.INVOICE_ID
AND a.ORG_ID = b.ORGANIZATION_ID
AND a.source = 'RMS';
IF v_check > 0 THEN
select name into v_instance from V$DATABASE;
x_email_to := 'danielliu#abc.com, vincentyuan#abc.com, christinewong#abc.com';
x_email_cc := 'andycheng#abc.com, tonyfok#abc.com';
c := utl_smtp.open_connection('LCEX7',25);
--utl_smtp.open_connection('smtp.office365.com',587);
--utl_smtp.helo(c, 'LCEX7');
utl_smtp.helo(c, 'LCEX7');
utl_smtp.mail(c, 'lc_ofin#abc.com');
-- utl_smtp.rcpt( c, x_email_to);
-- utl_smtp.rcpt( c, x_email_cc);
v_temp_str := replace(x_email_to, ',', ';');
v_temp_str := replace(x_email_cc, ',', ';');
while length(v_temp_str) > 0 loop
v_temp := instr(v_temp_str, ';', 1, 1);
if v_temp > 0 then
utl_smtp.rcpt(c, trim(substr(v_temp_str, 1, v_temp - 1)));
v_temp_str := substr(v_temp_str, v_temp + 1, length(v_temp_str));
else
utl_smtp.rcpt(c, trim(v_temp_str));
v_temp_str := null;
end if;
end loop;
utl_smtp.open_data(c);
utl_smtp.write_data(c,'From: "Oracle Fin (' || v_instance ||') " <lc_ofin#abc.com>' || utl_tcp.CRLF);
utl_smtp.write_data(c, 'To: ' || x_email_to || utl_tcp.CRLF);
utl_smtp.write_data(c, 'Cc: ' || x_email_cc || utl_tcp.CRLF);
utl_smtp.write_data(c, 'Subject: AP invoice reject list ' || to_char(sysdate,'MM/dd/yyyy HH:mm:ss') || utl_tcp.CRLF);
utl_smtp.write_data(c, 'MIME-Version:' || '1.0' || utl_tcp.CRLF);
utl_smtp.write_data(c,'Content-Type: ' || 'text/html; charset=utf-8' ||utl_tcp.CRLF);
utl_smtp.write_data(c, 'Content-Transfer-Encoding: ' || '"8Bit"' || utl_tcp.CRLF);
send_data('<html><body>');
send_data('<p>Dear All,</p>');
send_data('<p>Please find the enclosed AP reject list and advise. Thanks!</p>');
send_data('<table border=''0'' width=''100%'' cellpadding=''1'' cellspacing=''1''>');
send_data('<tr style=''font-size:75%;font-weight:bold;''>');
send_data('<td>AP ORG</td>
<td>ORG ID</td>
<td>Source</td>
<td>INVOICE ID</td>
<td>Reject Reason</td>
<td>Invoice Num</td>
<td>Invoice Date</td>
<td>GL Date</td>
<td>Vendor Name</td>
<td>Vendor Site Code</td>
<td>INVOICE AMOUNT</td>
<td>INVOICE CURRENCY CODE</td>
<td>EXCHANGE RATE</td>');
v_r1_row_num := 0;
open c1;
loop
fetch c1
into r1;
exit when c1%notfound;
if (mod(v_r1_row_num,2)=0) then
send_data('<tr style=''font-size:75%;background-color:#E0EBFF;''>');
else
send_data('<tr style=''font-size:75%;''>');
end if;
send_data('<td>' || r1.AP_ORG || '</td>
<td>' || r1.ORG_ID || '</td>
<td>' || r1.source || '</td>
<td>' || r1.INVOICE_ID || '</td>
<td>' || r1.Reject_Reason || '</td>
<td>' || r1.Invoice_Num || '</td>
<td>' || r1.Invoice_Date || '</td>
<td>' || r1.GL_Date || '</td>
<td>' || r1.Vendor_Name || '</td>
<td>' || r1.Vendor_Site_Code|| '</td>
<td>' || r1.INVOICE_AMOUNT || '</td>
<td>' || r1.INVOICE_CURRENCY_CODE || '</td>
<td>' || r1.EXCHANGE_RATE || '</td></tr>');
v_r1_row_num := v_r1_row_num + 1;
END LOOP;
CLOSE c1;
send_data('</table>');
send_data('<p>Best Regards,</p>');
send_data('<p>Technology Department</p>');
send_data('</body></html>');
utl_smtp.close_data(c);
utl_smtp.quit(c);
end if ;
END check_ap_reject_list;
You need a RCPT for each email recipient (whether CC or TO), and then a line with "To" and "Cc" where appropriatein the email data. So some pseudo code would be along the lines of:
declare
x_email_to sys.odcivarchar2list := sys.odcivarchar2list('danielliu#abc.com, vincentyuan#abc.com, christinewong#abc.com');
x_email_cc sys.odcivarchar2list := sys.odcivarchar2list('andycheng#abc.com, tonyfok#abc.com');
begin
...
... normal email initialisation (open,helo,etc)
...
for i in 1 .. x_email_to.count loop
utl_smtp.rcpt(l_conn, x_email_to(i));
end loop;
for i in 1 .. x_email_cc.count loop
utl_smtp.rcpt(l_conn, x_email_cc(i));
end loop;
for i in 1 .. x_email_to.count loop
utl_smtp.write_data(c, 'To: ' || x_email_to(i) || utl_tcp.CRLF);
end loop;
for i in 1 .. x_email_cc.count loop
utl_smtp.write_data(c, 'Cc: ' || x_email_cc(i) || utl_tcp.CRLF);
end loop;
...
... rest of email
...
end;
I'm using an array so it easy to loop through each email address. In your case, you can just parse the string looking for each comment, but the concept is the same.
One RCPT per recipient
One write_data per CC and TO
Connor's answer is spot on if you must use the utl_smtp package.
Unless you have a need to directly operate against the SMTP protocol, however, that's a relatively cumbersome way to send an email. In older versions of Oracle, that was the way you had to send emails. Even back in the 10g days, Oracle had the utl_mail.send procedure that let you pass in comma-separated lists of recipients for the to, cc, and bcc lists along with an HTML message
utl_mail.send( sender => 'reply-to#your-domain.com',
recipients => x_email_to,
cc => x_email_cc,
subject => 'Subject: AP invoice reject list ' ||
to_char(sysdate,'MM/dd/yyyy HH:mm:ss'),
message => x_html_document,
mime_type => 'text/html' );
No need to worry about appending all the CRLF's manually, no need to make dozens of calls to write_data and/or write_raw_data. Just build the HTML document you want to send in a local variable and send it. Oracle already built the infrastructure for 99% of use cases.
If you happen to have APEX installed in your database (even if you don't have an APEX application), you can also use the apex_mail package which has a similar send package. That version simplifies the process if you want to send a HTML and a plain text version of the same email so that users whose email clients don't support HTML can view it. And since apex_mail enqueues messages to be sent rather than talking directly to the SMTP server, apex_mail calls are transactional so they'll roll back if the parent transaction fails which is nice to avoid spamming internal users if this is a process that can be retried in the event of an unexpected error.

Find all columns that have emails in data oracle

I am going to start off by apologizing because I don't know how to put my problem into words.
i have a database with about 15000 columns and I want to find every column that has emails stored in it. I've tried searching through column names but that isn't helping because there is so much variation.
i want to do something like this
select column_name from all_tab_cols where data like '%#%.com%
this is on an oracle database but I am accessing the data through tableau.
Thanks,
Aayush
Clarification: I want to find every column that has an email address in it.
This will only find email addresses that end with #something.com, but you are looking for something like what is below. There have been other posts about finding email addresses, it is very difficult to do:
DECLARE
l_cmd VARCHAR2 (2000);
l_found INTEGER;
BEGIN
FOR eachcol IN ( SELECT *
FROM all_tab_cols a
WHERE a.data_type = 'VARCHAR2'
AND owner = 'SEARCHSCHEMANAME'
ORDER BY table_name, column_name)
LOOP
l_cmd :=
'select count(*) c from '
|| eachcol.owner
|| '.'
|| eachcol.table_name
|| ' where '
|| LOWER (eachcol.column_name)
|| q'[ LIKE '%#%.com%' AND ROWNUM = 1]';
EXECUTE IMMEDIATE l_cmd INTO l_found;
IF l_found > 0
THEN
DBMS_OUTPUT.put_line (
RPAD (eachcol.owner || '.' || eachcol.table_name || '.' || eachcol.column_name, 92)
|| ' may contain email addresses'
);
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (l_cmd);
DBMS_OUTPUT.put_line (SQLERRM);
RAISE;
END;

Emails sent through Oracle losing dots(.)

I have an oracle procedure that sends emails and there are some inconsistencies happening with dots(.) in the messages.
The problem is that in this specific case the email body is being built as an HTML and there is a link in the body of the email message and one of the dots is being removed from this link. For instance xxx.xxx.xxx is showing in the email as xxxxxx.xxx. This results in the receiver being unable to access the wanted link.
During my research for solutions to this problem I stumbled on the section 4.5.2 of SMTP standard RFC2821 that says:
When a line of mail text is received by the SMTP server, it checks
the line. If the line is composed of a single period, it is treated
as the end of mail indicator. If the first character is a period and
there are other characters on the line, the first character is
deleted.
But how do I deal with this in order to make the links in the emails work properly?
Below the code of the procedure used to send emails:
CREATE OR REPLACE PROCEDURE SEND_MAIL (p_from in VARCHAR2,
p_to in VARCHAR2,
p_subject in VARCHAR2,
p_message in VARCHAR2)
IS
lv_server VARCHAR2(255) := 'xxx.xxx.xxx.xxx';
lv_port NUMBER(10) := 25;
lv_rcpt VARCHAR2(255) := p_to;
lv_from VARCHAR2(255) := p_from;
lv_subject VARCHAR2(255) := p_subject;
lv_message VARCHAR2(20000) := p_message;
lv_conn UTL_SMTP.CONNECTION;
BEGIN
lv_Conn := UTL_SMTP.Open_Connection(lv_server, lv_port);
UTL_SMTP.Helo(lv_conn, lv_server);
UTL_SMTP.Mail(lv_conn, lv_from);
UTL_SMTP.Rcpt(lv_conn, lv_rcpt);
UTL_SMTP.OPEN_DATA(lv_conn);
UTL_SMTP.WRITE_DATA(lv_conn, 'Subject: =?ISO-8859-1?Q?' ||
UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.QUOTED_PRINTABLE_ENCODE(UTL_RAW.CAST_TO_RAW(lv_subject))) ||
'?=' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(lv_conn, 'MIME-version: 1.0' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(lv_conn, 'Content-Type: text/html;charset=iso-8859-1' || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(lv_conn, 'Content-Transfer-Encoding: quoted-printable '|| UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(lv_conn, 'From: ' || lv_from || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(lv_conn, 'To: ' || lv_rcpt || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(lv_conn, UTL_TCP.CRLF);
UTL_SMTP.WRITE_RAW_DATA(lv_conn, UTL_ENCODE.QUOTED_PRINTABLE_ENCODE(UTL_RAW.CAST_TO_RAW(Utl_Tcp.Crlf || lv_message)));
UTL_SMTP.WRITE_DATA(lv_conn, UTL_TCP.CRLF);
UTL_SMTP.CLOSE_DATA(lv_conn);
UTL_SMTP.QUIT(lv_conn);
END;

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

Resources