All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oreilly.servlet.MailMessage.maroney Maven / Gradle / Ivy

There is a newer version: 2024.5.0.0
Show newest version
// Copyright (C) 1999-2001 by Jason Hunter .
// All rights reserved.  Use of this class is limited.
// Please see the LICENSE for more information.

package com.oreilly.servlet;

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

/** 
 * A class to help send SMTP email.  It can be used by any Java program, not
 * just servlets.  Servlets are likely to use this class to:
 * 
    *
  • Send submitted form data to interested parties *
  • Send an email page to an administrator in case of error *
  • Send the client an order confirmation *
*

* This class is an improvement on the sun.net.smtp.SmtpClient class * found in the JDK. This version has extra functionality, and can be used * with JVMs that did not extend from the JDK. It's not as robust as * the JavaMail Standard Extension classes, but it's easier to use and * easier to install. *

* It can be used like this: *

 * String mailhost = "localhost";  // or another mail host
 * String from = "Mail Message Servlet ";
 * String to = "[email protected]";
 * String cc1 = "[email protected]";
 * String cc2 = "[email protected]";
 * String bcc = "[email protected]";
 *  
 * MailMessage msg = new MailMessage(mailhost);
 * msg.from(from);
 * msg.to(to);
 * msg.cc(cc1);
 * msg.cc(cc2);
 * msg.bcc(bcc);
 * msg.setSubject("Test subject");
 * PrintStream out = msg.getPrintStream();
 *  
 * Enumeration enum = req.getParameterNames();
 * while (enum.hasMoreElements()) {
 *   String name = (String)enum.nextElement();
 *   String value = req.getParameter(name);
 *   out.println(name + " = " + value);
 * }
 *  
 * msg.sendAndClose();
 * 
*

* Be sure to set the from address, then set the recepient * addresses, then set the subject and other headers, then get the * PrintStream, then write the message, and finally send and close. * The class does minimal error checking internally; it counts on the mail * host to complain if there's any malformatted input or out of order * execution. *

* An attachment mechanism based on RFC 1521 could be implemented on top of * this class. In the meanwhile, JavaMail is the best solution for sending * email with attachments. *

* Still to do: *

    *
  • Figure out how to close the connection in case of error *
* * @author Jason Hunter, Copyright © 1999 * @version 1.3, 2002/11/01, added methods send(), cancelAndClose(), * close(), and reset() (thanks to Colin Maroney) * @version 1.2, 2002/11/01, added logic to suppress CC: header if no CC addrs * @version 1.1, 2000/03/19, added angle brackets to address, helps some servers * @version 1.0, 1999/12/29 */ public class MailMessage { String host; String from; Vector to, cc; Hashtable headers; MailPrintStream out; BufferedReader in; Socket socket; boolean preData; /** * Constructs a new MailMessage to send an email. * Use localhost as the mail server. * * @exception IOException if there's any problem contacting the mail server */ public MailMessage() throws IOException { this("localhost"); } /** * Constructs a new MailMessage to send an email. * Use the given host as the mail server. * * @param host the mail server to use * @exception IOException if there's any problem contacting the mail server */ public MailMessage(String host) throws IOException { this.host = host; to = new Vector(); cc = new Vector(); headers = new Hashtable(); setHeader("X-Mailer", "com.oreilly.servlet.MailMessage (www.servlets.com)"); connect(); sendHelo(); } /** * Sets the from address. Also sets the "From" header. This method should * be called only once. * * @exception IOException if there's any problem reported by the mail server */ public void from(String from) throws IOException { sendFrom(from); this.from = from; } /** * Sets the to address. Also sets the "To" header. This method may be * called multiple times. * * @exception IOException if there's any problem reported by the mail server */ public void to(String to) throws IOException { sendRcpt(to); this.to.addElement(to); } /** * Sets the cc address. Also sets the "Cc" header. This method may be * called multiple times. * * @exception IOException if there's any problem reported by the mail server */ public void cc(String cc) throws IOException { sendRcpt(cc); this.cc.addElement(cc); } /** * Sets the bcc address. Does NOT set any header since it's a *blind* copy. * This method may be called multiple times. * * @exception IOException if there's any problem reported by the mail server */ public void bcc(String bcc) throws IOException { sendRcpt(bcc); // No need to keep track of Bcc'd addresses } /** * Sets the subject of the mail message. Actually sets the "Subject" * header. */ public void setSubject(String subj) { headers.put("Subject", subj); } /** * Sets the named header to the given value. RFC 822 provides the rules for * what text may constitute a header name and value. */ public void setHeader(String name, String value) { // Blindly trust the user doesn't set any invalid headers headers.put(name, value); } /** * Returns a PrintStream that can be used to write the body of the message. * A stream is used since email bodies are byte-oriented. A writer could * be wrapped on top if necessary for internationalization. * * @exception IOException if there's any problem reported by the mail server */ public PrintStream getPrintStream() throws IOException { setFromHeader(); setToHeader(); setCcHeader(); sendData(); preData = false; flushHeaders(); return out; } void setFromHeader() { setHeader("From", from); } void setToHeader() { setHeader("To", vectorToList(to)); } void setCcHeader() { if (!cc.isEmpty()) { // thanks to Patrice, [email protected] setHeader("Cc", vectorToList(cc)); } } String vectorToList(Vector v) { StringBuffer buf = new StringBuffer(); Enumeration e = v.elements(); while (e.hasMoreElements()) { buf.append(e.nextElement()); if (e.hasMoreElements()) { buf.append(", "); } } return buf.toString(); } void flushHeaders() throws IOException { // XXX Should I care about order here? Enumeration e = headers.keys(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String value = (String) headers.get(name); out.println(name + ": " + value); } out.println(); out.flush(); } /** * Sends the message and closes the connection to the server. * The MailMessage object cannot be reused. * * @exception IOException if there's any problem reported by the mail server */ public void sendAndClose() throws IOException { sendDot(); sendQuit(); // sender should always quit at end. disconnect(); } /** * Sends the message but does NOT close the connection. * The MailMessage object can optionally be reset using reset() and * re-used. * * @exception IOException if there is any problem reported by the mail server */ public void send() throws IOException { sendDot(); preData = true; } /** * Backs out of sending the message by calling QUIT. If you are in the * middle of sending message data (you have acquired the PrintStream) this * function will 'hang up' on the server so your message will not get sent. * The MailMessage object cannot be reused. * * @exception IOException if there's any problem reported by the mail server */ public void cancelAndClose() throws IOException { if (preData) { sendReset(); sendQuit(); } disconnect(); } /** * Closes the connection to the server, after all sends. After this call, * the MailMessage object cannot be reused anymore. If you are in the * middle of sending message data, this function will 'hang up' on the * server so your message will not get sent. * * @exception throws IOException if there is a problem with the server */ public void close() throws IOException { if (preData) { sendQuit(); } disconnect(); } /** * Attempts to reset the message, in effect throwing it away. * Can only be called BEFORE you start writing to the PrintStream * (actually, before you get the PrintStream). Allows reuse of * a MailMessage object that has not started writing a new message. * * @exception IOException if the PrintWriter has been obtained, or if * there is a problem with the server */ public void reset() throws IOException { if (preData) { sendReset(); } else { throw new IOException("Too late to reset"); } } // Make a limited attempt to extract a sanitized email address // Prefer text in , ignore anything in (parentheses) static String sanitizeAddress(String s) { int paramDepth = 0; int start = 0; int end = 0; int len = s.length(); for (int i = 0; i < len; i++) { char c = s.charAt(i); if (c == '(') { paramDepth++; if (start == 0) { end = i; // support "address (name)" } } else if (c == ')') { paramDepth--; if (end == 0) { start = i + 1; // support "(name) address" } } else if (paramDepth == 0 && c == '<') { start = i + 1; } else if (paramDepth == 0 && c == '>') { end = i; } } if (end == 0) { end = len; } return s.substring(start, end); } // * * * * * Raw protocol methods below here * * * * * void connect() throws IOException { socket = new Socket(host, 25); out = new MailPrintStream( new BufferedOutputStream( socket.getOutputStream())); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); getReady(); } void getReady() throws IOException { String response = in.readLine(); int[] ok = { 220 }; if (!isResponseOK(response, ok)) { throw new IOException( "Didn't get introduction from server: " + response); } } void sendHelo() throws IOException { String local = InetAddress.getLocalHost().getHostName(); int[] ok = { 250 }; send("HELO " + local, ok); } void sendFrom(String from) throws IOException { int[] ok = { 250 }; send("MAIL FROM: " + "<" + sanitizeAddress(from) + ">", ok); } void sendRcpt(String rcpt) throws IOException { int[] ok = { 250, 251 }; send("RCPT TO: " + "<" + sanitizeAddress(rcpt) + ">", ok); } void sendData() throws IOException { int[] ok = { 354 }; send("DATA", ok); } void sendDot() throws IOException { int[] ok = { 250 }; send("\r\n.", ok); // make sure dot is on new line } void sendQuit() throws IOException { int[] ok = { 221 }; send("QUIT", ok); } void sendReset() throws IOException { int[] ok = { 250 }; send("RSET", ok); } void send(String msg, int[] ok) throws IOException { out.rawPrint(msg + "\r\n"); // raw supports . //System.out.println("S: " + msg); String response = in.readLine(); //System.out.println("R: " + response); if (!isResponseOK(response, ok)) { throw new IOException( "Unexpected reply to command: " + msg + ": " + response); } } boolean isResponseOK(String response, int[] ok) { // Check that the response is one of the valid codes for (int i = 0; i < ok.length; i++) { if (response.startsWith("" + ok[i])) { return true; } } return false; } void disconnect() throws IOException { if (out != null) out.close(); if (in != null) in.close(); if (socket != null) socket.close(); } } // This PrintStream subclass makes sure that . becomes .. // per RFC 821. It also ensures that new lines are always \r\n. // class MailPrintStream extends PrintStream { int lastChar; public MailPrintStream(OutputStream out) { super(out, true); // deprecated, but email is byte-oriented } // Mac OS 9 does \r, but that's tough to distinguish from Windows \r\n. // Don't tackle that problem right now. public void write(int b) { if (b == '\n' && lastChar != '\r') { rawWrite('\r'); // ensure always \r\n rawWrite(b); } else if (b == '.' && lastChar == '\n') { rawWrite('.'); // add extra dot rawWrite(b); } else if (b != '\n' && lastChar == '\r') { // Special Mac OS 9 handling rawWrite('\n'); rawWrite(b); if (b == '.') { rawWrite('.'); // add extra dot } } else { rawWrite(b); } lastChar = b; } public void write(byte buf[], int off, int len) { for (int i = 0; i < len; i++) { write(buf[off + i]); } } void rawWrite(int b) { super.write(b); } void rawPrint(String s) { int len = s.length(); for (int i = 0; i < len; i++) { rawWrite(s.charAt(i)); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy