UTL_SMTP
UTL_SMTP has a richer functionality than the UTL_MAIL or APEX_MAIL packages; but that extra versatility comes with more complicated syntax. Before digging into the details of how the package works I’ll first show a short UTL_SMTP example, simply sending “Hello World!” to two recipients.
DECLARE crlf CONSTANT VARCHAR2(2) := UTL_TCP.crlf; v_conn UTL_SMTP.connection; BEGIN v_conn := UTL_SMTP.open_connection('my_server.mydomain.com'); UTL_SMTP.helo(v_conn, 'mydomain.com'); UTL_SMTP.mail(v_conn, 'sean.stuber@gmail.com'); UTL_SMTP.rcpt(v_conn, 'john.doe@example.com'); UTL_SMTP.rcpt(v_conn, 'jane.doe@example.com'); UTL_SMTP.open_data(v_conn); UTL_SMTP.write_data(v_conn, 'From: sean.stuber@gmail.com' || crlf); UTL_SMTP.write_data(v_conn, 'To: john.doe@example.com,'); UTL_SMTP.write_data(v_conn, ' jane.doe@example.com' || crlf); UTL_SMTP.write_data(v_conn, 'Subject: Test Subject' || crlf); UTL_SMTP.write_data(v_conn, crlf); UTL_SMTP.write_data(v_conn, 'Hello World!'); UTL_SMTP.close_data(v_conn); UTL_SMTP.quit(v_conn); END;
While it’s certainly a more verbose syntax compared to UTL_MAIL or APEX_MAIL; if you’re used to working with files or other handle-based APIs you can see it follows a similar structure: acquire a handle, send data to the handle, release the handle.
Breaking this example into pieces we can see how it compares to the UTL_MAIL implementation. First we identify and open a connection to the smtp server.
v_conn := UTL_SMTP.open_connection('my_server.mydomain.com');
This step is invisible to the user with UTL_MAIL, but recall one of the requirements for UTL_MAIL is specifying the SMTP_OUT_SERVER parameter. With UTL_SMTP you declare the server each time you use it. If it has been set, you could query v$parameter for the value and use it here too. With UTL_MAIL we don’t see the connection handle, but we know it must exist in some form in order to get the email message to the server.
Next we come to the HELO command. As the name implies, this is an introduction or handshake between our program and the smtp server. It is expected but not required to send the domain of your client to the server. You could send anything here: “sample.nowhere” would be equally valid by the SMTP protocol.
UTL_SMTP.helo(v_conn, 'mydomain.com');
It would be possible for the server to reject an unknown domain; but that is not normal behavior. The main purpose of HELO beyond the simple introduction is a declaration that there will be no attempt to authenticate to the server. If the server is configured to allow such communication you can begin transmitting data. Private smtp servers are often configured to allow unauthenticated transmission from clients within their private network. Public smtp servers will almost always require some form of authentication. I will provide examples of that in a follow up article. There is no direct equivalent of this step with UTL_MAIL; but therein lies the opportunity. As mentioned earlier, UTL_MAIL doesn’t support authentication or encrypted transmission via TLS. UTL_SMTP can support these features because it has a step between opening the connection and starting the mail message.
Once we have established our connection and the server has accepted our introduction the next step is declaring who is sending this email, i.e. the FROM address.
UTL_SMTP.mail(v_conn, 'sean.stuber@gmail.com');
After the from comes the TO addresses.
UTL_SMTP.rcpt(v_conn, 'john.doe@example.com'); UTL_SMTP.rcpt(v_conn, 'jane.doe@example.com');
It is important to note the SMTP protocol has no notion of CC or BCC lists. All recipients are equivalent as far as the transmission of an email message is concerned. Thus, for each recipient, whether TO, CC, or BCC, there will be one call to UTL_SMTP.rcpt.
Now that we have have established from who the email is coming and to whom it is being sent we can begin constructing the actual content of the email. This comes in two parts. The email header and the email body. The header starts with the open_data command and ends with a blank line to delineate it from the body. Each line of the header is terminated with a carriage-return and line-feed (defined as a constant for ease of use.) The “To” line is split into multiple WRITE_DATA calls simply for formatting in this article, in real code it would likely be a single call.
UTL_SMTP.open_data(v_conn); UTL_SMTP.write_data(v_conn, 'From: sean.stuber@gmail.com' || crlf); UTL_SMTP.write_data(v_conn, 'To: john.doe@example.com,'); UTL_SMTP.write_data(v_conn, ' jane.doe@example.com' || crlf); UTL_SMTP.write_data(v_conn, 'Subject: Test Subject' || crlf);
Here we can see the From and To spelled out again. Theses are essentially just comments as far as the SMTP protocol is concerned; the MAIL and RCPT commands above are the real determinants of the From and To lists. Some servers may read these; but, strictly speaking, they are not part of the SMTP protocol. Since the To line is not an actual command to the SMTP server, you could write the name of a mailing list instead of all of the individual recipients. As long as the recipients each had a RCPT command, they would get the message. The To would be a descriptive name. If you wanted to use CC and BCC lists, after defining each with an RCPT call, you would include a WRITE_DATA line of ‘CC: ….’ to list the carbon copied recipients and you would not create a BCC line, thus leaving the header blind to those recipients. After the From, To, and CC lines, we write out the subject line. If we wanted to set a priority we could do that with another WRITE_DATA command.
The header is complete with From, To, and Subject lines. Next is a blank line to end the header and start the body.
UTL_SMTP.write_data(v_conn, crlf);
Syntax-wise, the body is the same as the header, it’s just more calls to write_data. In this example the message is just one line, so we write it and close the body with the close_data command.
UTL_SMTP.write_data(v_conn, 'Hello World!');
UTL_SMTP.close_data(v_conn);
The close_data command tells the server the email message is complete. The server will then do whatever processing, if any, it needs to and send it to the recipient’s address. Since we are done with our message we can disconnect from the SMTP server using the quit command.
UTL_SMTP.quit(v_conn);
And that’s all there is to the UTL_SMTP example. It’s significantly more code than using UTL_MAIL; but, hopefully, the break down and explanation outlines the pattern and how it maps to the simpler UTL_MAIL syntax.
Using this same structure you can create email content of arbitrary length through the use of more write_data calls. Thus allowing you to exceed the varchar2 limits of UTL_MAIL, but there is much more we can do with UTL_SMTP and that additional functionality will be explored in subsequent articles.
This concludes the introduction to basic emailing. Hopefully you can use the example above as frameworks to send simple messages through your email servers. In the next article we’ll look at authentication and encrypted transmission.