Sending HTML mail via SMTP part 2

Jake Howlett, 30 March 2002

Category: Java; Keywords: SMTP Mail API Socket HTML

Okay so in the first article on this topic we've showed that it's possible to send mail through the Domino server and have it appear to the user in HTML format. What we need to do now is see exactly how we do this.d

At this point it's worth noting that the code we use is going to be Java. This is one of the times where we can show ways in which Java is, in more ways than one, far superior to its LotusScript partner. LotusScript is fine up to a point but when we need to go a step further and do things like accessing network protocols it's the turn of Java.

The third article in this series has an example database that shows how to use them. In the database I have added the required Java to a Script Library that is included in the Agent project-files. This agent is run as a WebQuerySave event when the user saves the form called Socket. Using the information in this form and some from the Application Settings view the agent has all it needs to create a fully-formed message. Let's look at how we do this.

The Code:

With Java we can create a new Socket to a specific port on a server and then pass data back and forth through this connection. In order to do this we first need to import the necessary code libraries:

import java.io.*;
import java.net.*;

Now we can set up the connection and the two required streams through which we post the data:
vSocket = new Socket( sHostAddress, 25);
vInStream = new BufferedReader( new InputStreamReader(vSocket.getInputStream()));
vOutStream = new DataOutputStream(vSocket.getOutputStream());

Once we have these two streams opened it's a simple case of sending the correct lines of text, in the correct order, out via the output stream. In the last article I showed the order in which each line needs to be sent to the SMTP server. What I didn't show was how the client expects certain results to be received from the server. Some lines of data can simply be sent to the server ad hoc but some need to be accompanied by a reply that lets the client know that it has received the command and all is okay. If this isn't the case then we handle the error by cancelling the message sending and returning an error to the browser.

To send a command to the server we use the sendCommand method:
protected void sendCommand( String cmd ) throws Exception {
 vOutStream.writeBytes( cmd + "\r\n" );
}

To check the server's response we use the checkReply method:
protected boolean checkReply( String sRspNum ) {
 String sResponse = null;
 sResponse = vInStream.readLine();
 return sResponse.startsWith( sRspNum + " " );
}

So, to send the DATA command, from which we expect to receive the code 354 to let us know it's okay to send the body, we would use:
sendCommand( "DATA" );
  if ( !checkReply( "354" ) ) 
   killSend( 1005, "Didn\'t like our DATA command!" );

Notice the extra method called killSend. This is used simply as a way to terminate the whole sending by throwing an error that returns the agent a code with which it can let the user know there was a problem and what it was.

Not all of the lines we send to the server need us to check the code returned. When this is the case we simply use the sendText method:
public void sendText( String text ) throws Exception {
 vOutStream.writeBytes( text + "\r\n" );
}

With which we can send repeated lines of text to the server, like so:
sendText( "<html>" );
sendText( "<body>" );

That's about all we need really. All that is required now to actually build the message is the code that lives in the WebQuerySave agent. Here it is:
PrintWriter pw = getAgentOutput();
String dbpath=null;
try {
  Session vNotesSession = getSession();
  AgentContext vNotesAgentContext = vNotesSession.getAgentContext();
  Database vNotesDatabase = vNotesAgentContext.getCurrentDatabase();
  View vNotesView = vNotesDatabase.getView("luASK");
  Document vNotesDocument = vNotesAgentContext.getDocumentContext( );			
  Document vSettingDoc = vNotesView.getDocumentByKey("App Setting\\SMTPHost");
			
  if (vSettingDoc != null)
   sSMTPHostAddress = vSettingDoc.getItemValueString("ASValue");

  String sToShow = vNotesDocument.getItemValueString("ToShow");
  Vector vToTrue = vNotesDocument.getItemValue("SendTo");
  String sFromShow = vNotesDocument.getItemValueString("FromShow"); 
  String sFromTrue = vNotesDocument.getItemValueString("From");;
  String sSubject = vNotesDocument.getItemValueString("Subject");
  Vector vBody = vNotesDocument.getItemValue("Body");
		
  dbpath = vNotesDocument.getItemValueString("DBPath");
		
  HtmlMessage msg = new HtmlMessage( sSMTPHostAddress );

  msg.sendMessage( sFromTrue, sFromShow, vToTrue, sToShow, sSubject, vBody );
			
  //Assuming all must be okay, let's let the user know.
  pw.println( "[/" + dbpath + "/message?OpenForm&msgid=1000]");
			
 } catch ( SMTPException e) {
  System.err.println( e.getMessage() );
  pw.println( "[/" + dbpath + "/message?OpenForm&msgid=" + e.getCode() + "]");
 } catch( Exception e) {
  e.printStackTrace();
 }

In the code we have created a new HtmlMessage object passing in to it the name of the server to use. We then call this object's sendMessage method and pass in to it all the information it requires. Notice that the list of addressees and the body are both Vectors. This is because the fields on the document are multi-value. We rely on the sendMessage method to convert these lists in to strings that it can use.

Considerations:

The example I've used in the database of a form with all the fields required in order to send a message isn't perfect. It works and is a good example of how this method can be used but it's not likely to happen this way in the real world.

How you use this code and in what situation is down to you and can suit any number of different circumstances. Hopefully the way in which I've built the example database means that it will be easy for you to tailor it to your own needs. In the next article on this topic I will discuss a powerful method of sending whole pages from a Domino database simply by providing the code with a URL.

Naming problems:

You don't have to be a genius to see that the names used in the SendTo field on the Socket form of the database are not what we are used to seeing. The format we need to use when talking to SMTP servers is name@domain. We aren't allowed any spaces in here. Hence for any user in the Domino Directory we need to know there ShortName and their mail domain. To do this for a user in the real world we are probably going to have to find their Person document via a DBLookup and extract these two fields from it.

Domino vs Exchange:

Despite my allegiance to the Domino server this is another occasion where I have to admit that Microsoft reign supreme.

If by now you've tried sending mail with my example database and have tried adding an <img> tag you may have had problems. The Notes Mail Client isn't too hot when it comes to handling URLs - especially if they are relative.

If you are working in an environment where Domino and Exchange cohabit you will find this a lot easier. When you use Exchange as the target SMTP server and the Outlook client to read the mail you can begin to get really fancy. The Outlook client is a lot better at handling the HTML and even, to some extent, JavaScript. This I expect is due to Outlook using IE as the rendering device for the mail whereas Notes uses its own methods.

When using Outlook there is an extra line of code supported in the mails header which lets us set the equivalent of the <base href> tag in HTML. To do this we add another line to the mail's header like:

Content-Base: http://www.codestore.info/A55692/store.nsf/;

Then in the HTML we can add HTML for an image element like this:

<a href="all?OpenView"><img src="codestore.gif" /></a>

This is extremely useful when we want to send a whole lot of HTML as I mentioned earlier by using a URL as the source of this code. This is what I'll be talking about in the next article on this topic. Don't hold your breath though, this could take me a week or so ;-)

<<< Part 1
Part 2
>>> Part 3