Recipe 19.6 Sending Mail Without Using JavaMail


Problem

You want to send mail, but don't want to require javax.mail.

Solution

This is a Really Bad Idea. You can implement the SMTP protocol yourself, but you shouldn't.

Discussion

Implementing an Internet protocol from the ground up is not for the faint of heart. To get it right, you need to read and study the requisite Internet RFC[2] pseudo-standards. I make no pretense that this mail sender fully conforms to the relevant RFCs; in fact, it almost certainly does not. The toy implementation here uses a simpler send-expect sequencing to keep in sync with the SMTP server at the other end. Indeed, this program has little to recommend it for serious use; I can only say that I had it around, and it's a good illustration of how simple a mail sender can be. Reading it may help you to appreciate the JavaMail API, which handles not just SMTP but also POP, IMAP, and many other protocols. Do not use this code in production; use the JavaMail API instead!

[2] RFC stands for "Request For Comments," a reflection on the community-based standards process that was the norm when the Internet was young.

The basic idea of SMTP is that you send requests like MAIL, FROM, RCPT, and DATA in ASCII over an Internet socket (see Recipe Recipe 16.1). Even if your mail contains 8- or 16-bit characters, the control information must contain only "pure ASCII" characters. This suggests either using the byte-based stream classes from java.io (see Recipe 10.1) or using Readers/Writers with ASCII encoding. Further, if the data contains 8- or 16-bit characters, it should be encoded using MIME (see Recipe 19.4). This trivial example uses only the ASCII character set to send a plain text message.

When I run this program, it traces the SMTP transaction in the same way sendmail does with the -v option under Unix (this resemblance is intentional). The <<< and >>> are not part of the protocol; they are printed by the program to show the direction of communication (>>> means outgoing, from client to server, and <<< means the opposite). Lines starting with these symbols are the actual lines that an SMTP client and server exchange. You may notice that the server sends lines with both a three-digit numeric code and a text message, while the client sends four-letter commands like HELO and MAIL to tell the server what do to. The data sent in response to the line beginning with code 354 (the actual mail message) is not shown.

daroad.darwinsys.com$ jr SmtpTalk localhost ian  + jikes +E SmtpTalk.java  + java SmtpTalk localhost ian  SMTP Talker ready  <<< 220 darwinsys.com ESMTP Sendmail 8.9.3/8.9.3; Thu, 23 Dec 1999 16:02:00 >>> HELO darwinsys.com  <<< 250 darwinsys.com Hello ian@localhost [127.0.0.1], pleased to meet you  >>> MAIL From:<MAILER-DAEMON@daroad.darwinsys.com>  <<< 250 <MAILER-DAEMON@daroad.darwinsys.com>... Sender ok  >>> RCPT To:<ian>  <<< 250 <ian>... Recipient ok  >>> DATA  <<< 354 Enter mail, end with "." on a line by itself  >>> .  <<< 250 QAA00250 Message accepted for delivery  >>> QUIT  <<< 221 darwinsys.com closing connection daroad.darwinsys.com$

The program, shown in Example 19-7, is straightforward, if not very elegant.

Example 19-7. SmtpTalk.java
import java.io.*; import java.net.*; import java.util.*; /**  * SMTP talker class, usable standalone (as a SendMail(8) backend)  * or inside applications such as JabaDex that need to send mail..  *  * OBSOLETE!! Use javax.mail instead, now that it's available!  *  */ public class SmtpTalk implements SysExits {     // SysExits is a simple interface that just defines the     // System.exit( ) codes to make this compatible with Sendmail.     BufferedReader is;     PrintStream os;     private boolean debug = true;     private String host;     /** A simple main program showing the class in action.      *      * TODO generalize to accept From arg, read msg on stdin      */     public static void main(String[] argv) {         if (argv.length != 2) {             System.err.println("Usage: java SmtpTalk host user");             System.exit(EX_USAGE);         }         try {             SmtpTalk st = new SmtpTalk(argv[0]);             System.out.println("SMTP Talker ready");             st.converse("MAILER-DAEMON@daroad.darwinsys.com",                  argv[1], "Test message", "Hello there");         } catch (SMTPException ig) {             System.err.println(ig.getMessage( ));             System.exit(ig.getCode( ));         }     }     /** Constructor taking a server hostname as argument.      */     SmtpTalk(String server) throws SMTPException {         host = server;         try {             Socket s = new Socket(host, 25);             is = new BufferedReader(                 new InputStreamReader(s.getInputStream( )));             os = new PrintStream(s.getOutputStream( ));         } catch (NoRouteToHostException e) {             die(EX_TEMPFAIL, "No route to host " + host);         } catch (ConnectException e) {             die(EX_TEMPFAIL, "Connection Refused by " + host);         } catch (UnknownHostException e) {             die(EX_NOHOST, "Unknown host " + host);         } catch (IOException e) {             die(EX_IOERR, "I/O error setting up socket streams\n" + e);         }     }     /** Send a command with an operand */     protected void send_cmd(String cmd, String oprnd) {         send_cmd(cmd + " " + oprnd);     }     /* Send a command with no operand */     protected void send_cmd(String cmd) {         if (debug)             System.out.println(">>> " + cmd);         os.print(cmd + "\r\n");     }     /** Send_text sends the body of the message. */     public void send_text(String text) {         os.print(text + "\r\n");     }     /** Expect (read and check for) a given reply */     protected boolean expect_reply(String rspNum) throws SMTPException {         String s = null;         try {             s = is.readLine( );         } catch(IOException e) {             die(EX_IOERR,"I/O error reading from host " + host + " " + e);         }         if (debug) System.out.println("<<< " + s);         return s.startsWith(rspNum + " ");     }     /** Convenience routine to print message & exit, like      * K&P error( ), perl die(1,), ...      * @param ret Numeric value to pass back      * @param msg Error message to be printed on stdout.      */     protected void die(int ret, String msg) throws SMTPException {         throw new SMTPException(ret, msg);     }     /** send one Mail message to one or more recipients via smtp       * to server "host".      */     public void converse(String sender, String recipients,         String subject, String body) throws SMTPException {         if (!expect_reply("220")) die(EX_PROTOCOL,             "did not get SMTP greeting");         send_cmd("HELO", "darwinsys.com");         if (!expect_reply("250")) die(EX_PROTOCOL,             "did not ack our HELO");         send_cmd("MAIL", "From:<"+sender+">");    // no spaces!         if (!expect_reply("250")) die(EX_PROTOCOL,             "did not ack our MAIL command");         StringTokenizer st = new StringTokenizer(recipients);         while (st.hasMoreTokens( )) {             String r = st.nextToken( );             send_cmd("RCPT", "To:<" + r + ">");             if (!expect_reply("250")) die(EX_PROTOCOL,                 "didn't ack RCPT " + r);         }         send_cmd("DATA");         if (!expect_reply("354")) die(EX_PROTOCOL,"did not want our DATA!");         send_text("From: " + sender);         send_text("To: " + recipients);         send_text("Subject: " + subject);         send_text("");         send_text(body + "\r");              send_cmd(".");         if (!expect_reply("250")) die(EX_PROTOCOL,"Mail not accepted");         send_cmd("QUIT");         if (!expect_reply("221")) die(EX_PROTOCOL,"Other end not closing down");     } }



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net