Lucky Dip Logo
Powered by PlusNet. PlusNet broadband.
Patrick's Lucky Dip
Home > PL/SQL > Mail Package

Patrick Haston
16 December 2008

Sending Email from the Database

The Problem

We needed to send email alerts from several of our applications. APEX (Oracle Application Express) has email functionality built into it, but not all our applications are built in APEX.

We also wished to send calendar appointments. I've documented how to send appointments as an article in it's own right.

The Solution for Email

We created a database package, lets call it MAIL_PKG (we didn't, but perhaps should have). It has two procedures: SEND_EMAIL and SEND_HTML_EMAIL. The first is used to send a simple text email while the second sends, you guessed it, a message with a html body. This is what the package header looked like:

create or replace package mail_pkg as
 
-------------------------------------------------------------------------------
-- SEND_MAIL is used to send a simple text email
  procedure send_mail
  (
      msg_to      in varchar2
    , msg_from    in varchar2
    , msg_subject in varchar2
    , msg_text    in varchar2  
  );
 
-------------------------------------------------------------------------------
-- SEND_HTML_MAIL is used to send a html email
  procedure send_html_mail
  (
      msg_to      in varchar2
    , msg_from    in varchar2
    , msg_subject in varchar2
    , msg_text    in varchar2
    , msg_html    in varchar2
  );

end snh_mail;

The body of the SEND_MAIL procedure is:

procedure send_mail
(
    msg_to      in varchar2
  , msg_from    in varchar2
  , msg_subject in varchar2
  , msg_text    in varchar2  
)
as
 
  c  utl_smtp.connection;
  mailhost    VARCHAR2(30) := 'the.mail.server';
 
begin
 
  c := utl_smtp.open_connection(mailhost, 25); -- SMTP on port 25    
  utl_smtp.helo(c, mailhost);
  utl_smtp.mail(c, msg_from);
  utl_smtp.rcpt(c, msg_to);
 
  utl_smtp.data(c, 'From: '    || msg_from     || utl_tcp.crlf ||
                   'To: '      || msg_to       || utl_tcp.crlf ||
                   'Subject: ' || msg_subject  || utl_tcp.crlf ||
                   ''          || utl_tcp.crlf || msg_text);
 
  utl_smtp.quit(c);
 
EXCEPTION
  WHEN UTL_SMTP.INVALID_OPERATION THEN
     dbms_output.put_line(' Invalid Operation in Mail attempt using UTL_SMTP.');
  WHEN UTL_SMTP.TRANSIENT_ERROR THEN
     dbms_output.put_line(' Temporary e-mail issue - try again'); 
  WHEN UTL_SMTP.PERMANENT_ERROR THEN
     dbms_output.put_line(' Permanent Error Encountered.'); 
 
end send_mail;

This procedure makes use of the UTL_SMTP package. There are two local variables: c for the connection to the mail server, and mailhost to point to the server. You can either enter this as a domain name or and IP address.

The first thing you need to do is open a connection to the mail server and then you can start sending it the information needed to assemble the email. The key part is the call to UTL_SMTP.DATA, where you send the content of the email. It's important to use the UTL_TCP.CRLF value for the carriage return / line feed pair.

That's it - it's quite simple really. The exception handling is there but we've never seen any exceptions raised yet (although I'm tempting fate now).

Sending HTML Emails

To send an email containing formatted html you need to do a little more work. Email was originally built as a simple text protocol so the standards around email have evolved within these restrictions. Here's the code

procedure send_html_mail
(
      msg_to      in varchar2
    , msg_from    in varchar2
    , msg_subject in varchar2
    , msg_text    in varchar2
    , msg_html    in varchar2
)
as
 
  c  utl_smtp.connection;
  mailhost    VARCHAR2(30) := 'the.mail.server';
  l_boundary      varchar2(255) default 'a1b2c3d4e3f2g1';
 
begin
 
  c := utl_smtp.open_connection(mailhost, 25); -- SMTP on port 25    
  utl_smtp.helo(c, mailhost);
  utl_smtp.mail(c, msg_from);
  utl_smtp.rcpt(c, msg_to);
 
  utl_smtp.data(c, 'From: '    || msg_from     || utl_tcp.crlf ||
                   'To: '      || msg_to       || utl_tcp.crlf ||
                   'Subject: ' || msg_subject  || utl_tcp.crlf ||
                   'Content-Type: multipart/alternative; boundary=' ||
                     chr(34) || l_boundary ||  chr(34) || utl_tcp.crlf ||
                   '--' || l_boundary || utl_tcp.crlf ||
                   'content-type: text/plain; charset=us-ascii' ||
                   utl_tcp.crlf || utl_tcp.crlf ||
                   ''           || msg_text ||
                   utl_tcp.crlf || utl_tcp.crlf ||
                   '--' || l_boundary || utl_tcp.crlf ||
                   'content-type: text/html;' ||
                   utl_tcp.crlf || utl_tcp.crlf ||
                   ''           || msg_html || utl_tcp.crlf ||
                   '--' ||  l_boundary || '--' || utl_tcp.crlf);
 
  utl_smtp.quit(c);
 
EXCEPTION
  WHEN UTL_SMTP.INVALID_OPERATION THEN
     dbms_output.put_line(' Invalid Operation in Mail attempt    
                            using UTL_SMTP.');
  WHEN UTL_SMTP.TRANSIENT_ERROR THEN
     dbms_output.put_line(' Temporary e-mail issue - try again'); 
  WHEN UTL_SMTP.PERMANENT_ERROR THEN
     dbms_output.put_line(' Permanent Error Encountered.'); 
 
end send_html_mail;
 

The first difference is the addition of the Content-Type instruction. This is a MIME command (Multipart Internet Mail Extensions if you really must know) that does just that - allows email to contain multiple sections with different content. The content is seperated using the boundary variable. The first part is the plain text for those who don't use html browsers whilst the second part contains the nicely formatted html version.

I've also documented the procedure to send an appointment.