What is SMTP?

advertisement
Sending Email via the Database – Using utl_smtp
By Bradley D. Brown

What is SMTP?
SMTP stands for SIMPLE MAIL TRANSFER
PROTOCOL. This is the protocol that was
developed to allow people around the world to
exchange electronic mail. In other words, it’s
the behind the scenes protocol that handles this
for us.
What is utl_smtp?
Oracle provides a collection of database
packages to simplify our database development
using Oracle products. Utl stands for utility and
you know what SMTP stands for. In other
words, this is an email utility that provides us
with the ability to email from the database. Now
this is powerful stuff! In other words, you can
dynamically generate email from the database
and you can dynamically send it to different
people based on different criteria. Intelligent
email capabilities galore.
The email that you send can be sent as a standard
ASCII text email or an enhanced HTML email.
Most anything is possible!
This package provides SMTP client-side access
functionality to PL/SQL programs so they can
send electronic mails via SMTP. It does not
allow the PL/SQL program to receive e-mails.
The SMTP protocol is defined in RFC 821 and
RFC 1869 – to get more information on this
protocol, you can search for these RFCs.
Utl_smtp provides an API directly to the SMTP
protocol.





Why both Procedures and
Functions?
You may notice that most of the SMTP API has
a function form and a procedure form. You
might ask, why is this? The function form
returns the reply message after the command is
sent. The reply message is in the form of "XXX
<an optional reply message>", where XXX is the
reply code. The procedure form of the same API
calls the function form of the API, checks the
reply code and raises transient_error or
permanent_error exception if the reply code is in
the 400 or 500 range. The function form of the
API does not raise either of the two exceptions.
In other words, it’s up to you to process the reply
code accordingly.
Exceptions
There are three different exceptions that can be
generated by any of the procedure/function calls.
They are:

Call Sequence
When connecting to an SMTP server, you must
follow a pretty standard call sequence – in other
words, the order in which things are processed.
An SMTP connection is initiated by a call to
open_connection, which returns a SMTP
connection with the SMTP server. After a
connection is established, the following
procedure/function calls (of utl_smtp) are
required to send a mail:

helo() or ehlo() - identify the
domain of the sender
mail() - start a mail, specify the
sender
rcpt() - specify the recipient
open_data() - start the mail body
write_data() - write the mail
header/body (multiple calls)
close_data() - close the mail body
and send the mail
quit() - close the SMTP connection


invalid_operation - Operation is
invalid or specified in the wrong
call sequence - such as calling API
other
than
write_data(),
write_raw_data() or close_data()
after open_data() is called or
calling
write_data(),
write_raw_data() or close_data()
without first calling open_data()
transient_error - Transient server
error in 400 range
permanent_error
Permanent
server error in 500 range
Reply Codes
As mentioned, for each call to the SMTP, a reply
code will be passed back to your program unit.
The 200-300 codes are really just informational
messages, not errors. The 400-500 codes are
error messages. The following list contains each
of the reply codes that could be returned:


















211 - System status, or system help
reply
214 - Help message - [Information
on how to use the receiver or the
meaning of a particular nonstandard command; this reply is
useful only to the human user]
220 - <domain> Service ready
221 - <domain> Service closing
transmission channel
250 - Requested mail action okay,
completed
251 - User not local; will forward
to <forward-path>
354 - Start mail input; end with
<CRLF>.<CRLF>
421 - <domain> Service not
available, closing transmission
channel [This may be a reply to any
command if the service knows it
must shut down] 450 Requested
mail action not taken: mailbox
unavailable [E.g., mailbox busy]
451 - Requested action aborted:
local error in processing
452 - Requested action not taken:
insufficient system storage
500 - Syntax error, command
unrecognized - [This may include
errors such as command line too
long]
501 - Syntax error in parameters or
arguments
502 - Command not implemented
503 - Bad sequence of commands
504 - Command parameter not
implemented
550 - Requested action not taken:
mailbox unavailable [E.g., mailbox
not found, no access]
551 - User not local; please try
<forward-path>
552 - Requested mail action
aborted:
exceeded
storage
allocation


553 - Requested action not taken:
mailbox name not allowed - [E.g.,
mailbox syntax incorrect]
554 - Transaction failed
Example 1 – Send Hello
World!
Enough chatter about SMTP, let’s look at some
actual code that will simply send an email to a
user via the utl_smtp API:
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('smtpserver.tusc.com');
utl_smtp.helo(c, ‘tusc.com');
utl_smtp.mail(c, 'sender@tusc.com');
utl_smtp.rcpt(c,
'recipient@wherever.com');
utl_smtp.open_data(c);
send_header('From',
'"Sender"
<sender@tusc.com>');
send_header('To',
'"Recipient"
<recipient@wherever.com>');
send_header('To',
'"Recipient"
<recipient@wherever.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
utl_smtp.quit(c);
raise_application_error(-20000,
'Failed
to
send
mail
due
to
the
following error: ' || sqlerrm);
END;
Example 2 - Send a Message
from Anyone to Anyone
This next example shows a generic procedure
that will send mail from anyone to anyone. All
of the parameters are passed into the procedure.
This could be a very useful procedure for you:
procedure send_mail
(in_mail_server
varchar2
default
'smtp.ix.netcom.com',
in_sender_email
varchar2
default
‘me@tusc.com',
in_sender_name varchar2 default ‘My Full
Name',
in_recipient_email
varchar2
default
‘you@tusc.com',
in_recipient_name varchar2 default ‘Your
Full Name',
in_html_flg varchar2 default 'N',
in_subject varchar2 default 'No Subject
was Provided',
in_importance varchar2 default 'Normal',
in_body varchar2 default 'No Body for
this Email‘
)
as
-- Open the Connection, Set Mime Type
nbt_connection utl_smtp.connection;
nbt_message varchar2(30000);
BEGIN
nbt_connection :=
utl_smtp.open_connection(in_mail_server);
utl_smtp.helo(nbt_connection,
in_mail_server);
utl_smtp.mail(nbt_connection,
in_sender_email);
utl_smtp.rcpt(nbt_connection,
in_recipient_email);
utl_smtp.open_data(nbt_connection
);
if in_html_flg != 'N' then
-- Content-Type: text/html
utl_smtp.write_data(nbt_connection,
'Content-Type:
text/html'
||
utl_tcp.CRLF);
end if;
-- Set Header Info (From, To, Subject)
nbt_message := 'From: ' || '"' ||
in_sender_name
||
'"
<'
||
in_sender_email || '>';
utl_smtp.write_data(nbt_connection,
nbt_message || utl_tcp.CRLF);
nbt_message
:=
'To:
'
||
'"'
||
in_recipient_name
||
'"
<'
||
in_recipient_email || '>';
utl_smtp.write_data(nbt_connection,
nbt_message || utl_tcp.CRLF);
nbt_message := 'Subject: ' || in_subject;
utl_smtp.write_data(nbt_connection,
nbt_message || utl_tcp.CRLF);
-- Set Importance, Write Body
if in_importance != 'Normal' then -Normal is the default
nbt_message := 'Importance: ' ||
in_importance;
utl_smtp.write_data(nbt_connection,
nbt_message || utl_tcp.CRLF);
end if;
-- Write the body, but first end the
header
if length(in_body) <= 2000 then
utl_smtp.write_data(nbt_connection,
utl_tcp.CRLF || in_body);
else
utl_smtp.write_data(nbt_connection,
utl_tcp.CRLF);
for
i
in
1
round(length(in_body)/2000) loop
utl_smtp.write_data(nbt_connection,
..
substr(in_body,
((i1)*2000)+1, 2000));
end loop;
end if;
-- Wrap up and Error Processing
utl_smtp.close_data(nbt_connectio
n);
utl_smtp.quit(nbt_connection);
EXCEPTION
WHEN others then
utl_smtp.transient_error OR
utl_smtp.permanent_error THEN
utl_smtp.quit(nbt_connection);
error_routine('Failed
to
send
mail due to
the following error:' || sqlerrm);
END;
Example 3 - Pulling Data from
Database
Using the above procedure to send an email
message, we are going to dynamically pull
information (a report) from the database to
generate an HTML email message. Notice that
we use the htf package to build the HTML.
After building the list of employees, we send the
report to each of the employees on the list:
procedure email_employee_list
as
nbt_body varchar2(30000) := ‘’;
cursor
emp_cur
is
select
*
from
scott.emp;
BEGIN
-- Build subject
nbt_body
:=
nbt_body
||
htf.htmlOpen;
nbt_body
:=
nbt_body
||
htf.headOpen;
nbt_body
:=
nbt_body
||
htf.title( 'List of Employees');
nbt_body
:=
nbt_body
||
htf.headClose;
nbt_body
:=
nbt_body
||
htf.bodyOpen( cattributes =>
' bgcolor="#6666FF"
text="#FFFF00"' );
-- Building the Body
nbt_body := nbt_body || htf.para;
nbt_body := nbt_body || htf.header( 1,
'Current Employee List' ||
htf.br ||
htf.img(
'http://www.tusc.com/images/book3.gif',
cattributes => ' width="130"
height="160"'), calign => 'center');
nbt_body
:=
nbt_body
||
'<CENTER>';
nbt_body := nbt_body || htf.para;
nbt_body := nbt_body || 'Current
Employee List';
nbt_body
:=
nbt_body
||
htf.tableOpen( cattributes =>
' width="75%" border="1"' );
nbt_body
:=
nbt_body
htf.tableRowOpen;
nbt_body
:=
nbt_body
htf.tableHeader( 'Name');
nbt_body
:=
nbt_body
htf.tableHeader( 'Dept Number');
nbt_body
:=
nbt_body
htf.tableHeader( 'Salary');
nbt_body
:=
nbt_body
htf.tableRowClose;
-- For Loop
for emp_rec in emp_cur loop
nbt_body
:=
nbt_body
htf.tableRowOpen;
nbt_body
:=
nbt_body
htf.tableData(
||
||
departmental list to each employee or do just
about anything I want. Now that is powerful
push technology, isn’t it?
||
||
||
||
||
htf.anchor('http://tuscbdb/update_emp_sal
?in_emp_no='||
emp_rec.empno,emp_rec.ename));
nbt_body
:=
nbt_body
||
htf.tableData( emp_rec.deptno);
nbt_body
:=
nbt_body
||
htf.tableData( emp_rec.sal);
nbt_body
:=
nbt_body
||
htf.tableRowClose;
end loop;
nbt_body := nbt_body || htf.tableClose;
nbt_body := nbt_body || '</CENTER>';
nbt_body := nbt_body || htf.bodyClose;
nbt_body := nbt_body || htf.htmlClose;
-- Send to All the Employees
for emp_rec in emp_cur loop
send_mail('smtp.ix.netcom.com',
‘hr@tusc.com',
‘Human
Resources',
emp_rec.email, emp_rec.ename,
'Y', ‘Current Employee List',
'High', nbt_body);
end loop;
END;
Let’s look at the Email
After running this procedure, if I pull up the
email in Outlook, I see the following HTML
email message:
Detailed Reference
Let’s break down each of the program units that
are part of utl_smtp in greater detail.
open_connection
open_connection opens a SMTP connection to a
SMTP server. When a connection is made
successfully, the SMTP host name and port
number will be stored in the connection.
Parameters



host - SMTP host name to connect
to
port - port number of the SMTP
server to connect to
c - SMTP connection (OUT)
Return value
SMTP connection when connection is
established, or the SMTP reply (welcome)
message - 220 MIT-AI.ARPA Simple Mail
Transfer Service Ready
Syntax
FUNCTION open_connection
(host IN VARCHAR2,
port IN PLS_INTEGER DEFAULT 25,
c OUT connection)
RETURN reply;
FUNCTION open_connection
(host IN VARCHAR2,
port IN PLS_INTEGER DEFAULT 25)
RETURN connection;
Sample code
Here is a sample call to open_connection:
declare
c utl_smtp.connection :=
utl_smtp.open_connection('smtpserver.tusc.com');
Very impressive, isn’t it?
The ability to
dynamically build the HTML and dynamically
send the email to a group of people is very
powerful. I could easily enhance this to send a
declare
c utl_smtp.connection;
begin
c
:=
utl_smtp.open_connection(in_smtp_server);
Parameters
helo
Sends HELO command. At the time the
transmission channel is opened there is an
exchange to ensure that the hosts are
communicating with the hosts they think they are
communicating with. This command is used to
identify the sender-SMTP to the receiver-SMTP.
The argument field contains the host name of the
sender-SMTP. The receiver-SMTP identifies
itself to the sender-SMTP in the connection
greeting reply, and in the response to this
command.


c - SMTP connection
domain - domain of the sender
Return
SMTP reply
Syntax
FUNCTION ehlo
(c IN OUT NOCOPY connection,
domain IN VARCHAR2)
RETURN replies;
Parameters


c - SMTP connection
domain - domain of the sender
Return
SMTP reply (for
server.tusc.com)
example:
250
smtp-
PROCEDURE ehlo
(c IN OUT NOCOPY connection,
domain IN VARCHAR2);
Sample Function Call
declare replies utl_smtp.replies;
replies := utl_smtp.ehlo(c,’tusc.com’);
Syntax
Sample Procedure Call
FUNCTION helo
(c IN OUT NOCOPY connection,
domain IN VARCHAR2)
RETURN reply;
utl_smtp.ehlo(c, ‘tusc.com');
PROCEDURE helo
(c IN OUT NOCOPY connection,
domain IN VARCHAR2);
Sample Function Call
declare replies utl_smtp.replies;
replies := utl_smtp.helo(c,’tusc.com’);
mail
Sends MAIL command – this is the sender. The
first step in the procedure is the MAIL
command. The <sender> contains the source
mailbox. This command is used to initiate a mail
transaction in which the mail data is delivered to
one or more mailboxes. The argument field
contains a reverse-path.
Parameters
Sample Procedure Call
utl_smtp.helo(c, ‘tusc.com');



ehlo
Sends EHLO command.
A client SMTP
supporting SMTP service extensions should start
an SMTP session by issuing the EHLO
command instead of the HELO command. If the
SMTP server supports the SMTP service
extensions, it will give a successful response
(250), a failure response (550), or an error
response (500, 501, 502, 504, or 421). If the
SMTP server does not support any SMTP
service extensions, it will generate an error
response.
c - SMTP connection
sender - the sender
parameters
the
optional
parameters to MAIL command
Return
SMTP reply
Syntax
FUNCTION mail
(c IN OUT NOCOPY connection,
sender IN VARCHAR2,
parameters IN VARCHAR2 DEFAULT NULL)
RETURN reply;
PROCEDURE mail
(c IN OUT NOCOPY connection,
sender IN VARCHAR2,
parameters IN VARCHAR2 DEFAULT NULL);
Sample Procedure Call
Sample Function Call
open_data
declare reply utl_smtp.reply;
reply
:=
utl_smtp.mail(c,
'sender@tusc.com');
Sample Procedure Call
utl_smtp.mail(c, 'sender@tusc.com');
utl_smtp.rcpt(c,
'recipient@wherever.com');
Sends DATA command. This call opens the data
session that the caller makes subsequent
write_data() calls to write large piece of data,
following by close_data() to close the data
session. Basically ends the header section of the
SMTP transaction.
rcpt
Parameters
Sends RCPT command – this is the recipient.
This command gives a forward-path identifying
one recipient. If accepted, the receiver-SMTP
returns a 250 OK reply and stores the forwardpath. If the recipient is unknown, the receiverSMTP returns a 550 Failure reply. This second
step of the procedure can be repeated any
number of times. This command is used to
identify an individual recipient of the mail data;
multiple recipients are specified by multiple use
of this command.
c - SMTP connection
Parameters



c - SMTP connection
recipient - the recipient
parameters
the
optional
parameters to RCPT command
Return
SMTP reply
Syntax
FUNCTION open_data
(c IN OUT NOCOPY connection)
RETURN reply;
PROCEDURE open_data
(c IN OUT NOCOPY connection);
Sample Function Call
declare reply utl_smtp.reply;
reply := utl_smtp.open_data(c);
Return
Sample Procedure Call
SMTP reply
utl_smtp.open_data(c);
Syntax
write_data
FUNCTION rcpt
(c IN OUT NOCOPY connection,
recipient IN VARCHAR2,
parameters IN VARCHAR2 DEFAULT NULL)
RETURN reply;
Sends data. This call must be preceded by the
call open_data(). This command is used to
initiate a mail transaction in which the mail data
is delivered to one or more terminals. The
argument field contains a reverse-path. This
command is successful if the message is
delivered to a terminal.
PROCEDURE rcpt
(c IN OUT NOCOPY connection,
recipient IN VARCHAR2,
parameters IN VARCHAR2 DEFAULT NULL);
Sample Function Call
declare reply utl_smtp.reply;
reply
:=
utl_smtp.rcpt(c,
'recipient@wherever.com');
Parameters


Return
None
c - SMTP connection
data - the data body
Syntax
quit
PROCEDURE write_data(c IN OUT NOCOPY
connection, data IN VARCHAR2);
Sends QUIT command – transmission channel
closing.
This command specifies that the
receiver must send an OK reply and then close
the transmission channel.
Sample Procedure Calls
utl_smtp.write_data(c,
'Hello, world!');
utl_tcp.CRLF
||
Parameters
if length(in_body) <= 2000 then
c - SMTP connection
utl_smtp.write_data(nbt_connection,
utl_tcp.CRLF || in_body);
else
utl_smtp.write_data(nbt_connection,
utl_tcp.CRLF);
for
i
in
1
..
round(length(in_body)/2000) loop
Return
utl_smtp.write_data(nbt_connection,
substr(in_body, ((i-1)*2000)+1, 2000));
end loop;
end if;
FUNCTION quit
(c IN OUT NOCOPY connection)
RETURN reply;
close_data
PROCEDURE quit
(c IN OUT NOCOPY connection);
Sends DATA command. This call opens the data
session that the caller makes subsequent
write_data() calls to write large piece of data,
following by close_data() to close the data
session.
Parameters

c - SMTP connection
Return
SMTP reply
Syntax
FUNCTION close_data
(c IN OUT NOCOPY connection)
RETURN reply;
PROCEDURE close_data
(c IN OUT NOCOPY connection);
SMTP reply (221 smtp-server.tusc.com Service
closing transmission channel)
Syntax
Sample Function Call
declare reply utl_smtp.reply;
reply := utl_smtp.quit(c);
Sample Procedure Call
utl_smtp.quit(c);
rset
Sends RSET command. This command specifies
that the current mail transaction is to be aborted.
Any stored sender, recipients, and mail data must
be discarded, and all buffers and state tables
cleared. The receiver must send an OK reply.
Parameters

Return
Sample Function Call
SMTP reply
declare reply utl_smtp.reply;
reply := utl_smtp.close_data(c);
Syntax
Sample Procedure Call
utl_smtp.close_data(c);
c - SMTP connection
FUNCTION rset
(c IN OUT NOCOPY connection)
RETURN reply;
PROCEDURE rset
(c IN OUT NOCOPY connection);
Sample Function Call
help
declare reply utl_smtp.reply;
reply := utl_smtp.rset(c);
Sends HELP command. This command causes
the receiver to send helpful information to the
sender of the HELP command. The command
may take an argument (e.g., any command name)
and return more specific information as a
response.
Sample Procedure Call
utl_smtp.rset(c);
vrfy
Sends VRFY command, which allows you to
perform a fuzzy search for the username. This
command asks the receiver to confirm that the
argument identifies a user. If it is a user name,
the full name of the user (if known) and the fully
specified mailbox are returned.
Note –
Microsoft Exchange delivers the message “252
Cannot verify user” when vrfy is turned off. In
other words, it returns a success code, but
doesn’t do anything.
Parameters


c - SMTP connection
recipient - the recipient to verify
Parameters


c - SMTP connection
command - the command to get
help message
Return
SMTP reply
Syntax
FUNCTION help
(c IN OUT NOCOPY connection,
command IN VARCHAR2 DEFAULT NULL)
RETURN replies;
Sample Function Call
Return
declare replies utl_smtp.replies;
replies := utl_smtp.help(nbt_connection,
'VRFY');
SMTP reply
Syntax
Sample Output
214
Commands:
214 HELO MAIL RCPT DATA RSET
214 NOOP QUIT HELP VRFY ETRN
214
XEXCH50
STARTTLS
AUTH
214 End of HELP info
FUNCTION vrfy
(c IN OUT NOCOPY connection,
recipient IN VARCHAR2)
RETURN reply;
Fuzzy Search w/ vrfy
noop
reply := utl_smtp.vrfy(c, ‘Smith’);
Sample
reply:
<Smith@tusc.com>
250
Fred
Smith
reply := utl_smtp.vrfy(c, ‘Smithy’);
Sample reply: 251 User not local; will forward to
<Smithy@denver.tusc.com>
reply := utl_smtp.vrfy(c, ‘Jones’);
Sample reply: 550 String does not match
anything.
reply := utl_smtp.vrfy(c, ‘Jonesy’);
Sends NOOP command. This command does
not affect any parameters or previously entered
commands. It specifies no action other than the
receiver should send an OK reply.
Parameters

c - SMTP connection
Return
SMTP reply
Sample reply: 551 User not local; please try
<Jones@denver.tusc.com>
Syntax
reply
:=
‘Gourzenkyinplatz’);
FUNCTION noop
(c IN OUT NOCOPY connection)
RETURN reply;
utl_smtp.vrfy(c,
Sample reply: 553 User ambiguous.
Parameters
PROCEDURE noop
(c IN OUT NOCOPY connection);



Sample Function Call
declare reply utl_smtp.reply;
reply := utl_smtp.noop(c);
Return
SMTP reply
Sample Procedure Call
utl_smtp.noop(c);
Syntax
data
Sends DATA command. The data will be closed
by the sequence <CR><LF>.<CR><LF>. If
accepted, the receiver-SMTP returns a 354
Intermediate reply and considers all succeeding
lines to be the message text. When the end of
text is received and stored the SMTP-receiver
sends a 250 OK reply. The receiver treats the
lines following the command as mail data from
the sender. This command causes the mail data
from this command to be appended to the mail
data buffer. The mail data may contain any of the
128 ASCII character codes.
Parameters


c - SMTP connection
body - the data body
FUNCTION command
(c IN OUT NOCOPY connection,
cmd IN VARCHAR2,
arg IN VARCHAR2 DEFAULT NULL)
RETURN reply;
PROCEDURE command
(c IN OUT NOCOPY connection,
cmd IN VARCHAR2,
arg IN VARCHAR2 DEFAULT NULL);
command_replies
Sends a generic SMTP command and retrieves
multiple reply lines. Could be used for VRFY,
EXPN, TURN.
Parameters
Return
SMTP
reply
after
the
<CR><LF><CR><LF> is sent
c - SMTP connection
cmd – SMTP command
arg - optional argument to the
SMTP command
sequence
Syntax
FUNCTION data
(c IN OUT NOCOPY connection,
body IN VARCHAR2)
RETURN reply;



c - SMTP connection
cmd - SMTP command
arg - optional argument to the
SMTP command
Return
SMTP reply
Syntax
PROCEDURE data
(c IN OUT NOCOPY connection,
body IN VARCHAR2);
FUNCTION command_replies
(c IN OUT NOCOPY connection,
cmd IN VARCHAR2,
arg IN VARCHAR2 DEFAULT NULL)
RETURN replies;
command
EXPN – Expand List
Sends a generic SMTP command and retrieves a
single reply line. If multiple reply lines are
returned from the SMTP server, the last reply
line is returned. Could be used for VRFY and
TURN.
This command asks the receiver to confirm that
the argument identifies a mailing list, and if so,
to return the membership of that list. The full
name of the users (if known) and the fully
specified mailboxes are returned in a multiline
reply.
Command Replies Example
Biography
declare replies utl_smtp.replies;
replies := utl_smtp.command_replies
(c, ‘EXPN’, ‘Management’)
Bradley D. Brown is Chairman and Chief
Architect of TUSC (The Ultimate Software
Consultants), a 1998 Inc. 500 full-service
consulting company specializing in Oracle with
offices in Chicago, Denver,
Detroit and
Milwaukee. In June of 1998, Brown authored
the title Oracle Application Server Web Toolkit
Reference that is part of the Oracle Press Tips &
Techniques series TUSC is writing for
Osborne/McGraw-Hill.
His second book,
Oracle8i Web Development, came out in
December 1999. Brad has been working with
management information systems for more than
18 years, including the last 13 with a focus on
Oracle.
Sample reply (replies(i).code || replies(i).text):
250-Bradley
D.
Brown
<bradley.brown@denver.tusc.com>
250-Richard
J.
Niemiec
<richard.niemiec@chicago.tusc.com>
250-Joseph
C.
Trezzo
<joseph.trezzo@chicago.tusc.com>
Note – the mail server administrator can choose
not to implement this feature. If the feature is
not implemented, the following error will be
returned:
502-command not implemented
References
The following references are helpful for SMTP:
SMTP RFC 821 Specification
http://info.broker.isi.edu/innotes/rfc/files/rfc821.txt
http://www.landfield.com/rfcs/rfc821.html
SMTP RFC 1869 Specification
(SMTP Extensions)
http://info.broker.isi.edu/innotes/rfc/files/rfc1869.txt
http://www.xyweb.com/rfc/rfc1869.html
SMTP Header
http://www.arsdigita.com/asj/mime/
http://www.faqs.org/rfcs/rfc822.html
http://www.faqs.org/rfcs/rfc1049.html
Summary
I’m confident that after reading all about
utl_smtp, you can see that this is a powerful
utility and capability. The ability to dynamically
build emails and the ability to dynamically send
to users in the database is power! You can use
this to email passwords to users, send reports,
and push any information you wish to distribute.
So are you excited yet?
Download