import java.util.*; import java.net.*; //import msql.*; import java.io.*; /** * *** New in Version 2: *** * - Stream Input to Mailobject * - Add MIME Attachements to mail object * * SMTP Mailing Object. Allows sending mails to any smtpd. Possibilities to set varios detail information. * Also, there is detailed exception handling for paranoids. Implements complete SMTP handshake.
* Following Mail structure generated to the smtp server is something like:
*
  *   HELO  
  *   MAIL FROM: 
  *   RCPT TO: 
  *   DATA
  *     From: name_of_from 
  *     Sender: name_of_sender 
  *     To: name_of_to 
  *     Reply-To: name_of_replyto 
  *     cc: name_of_cc 
  *     cc: name_of_cc      (...there can be many cc's...)
  *     bcc: name_of_bcc 
  *     bcc: name_of_bcc      (...there can be many bcc's...)
  *       (...one delimiting empty line ...)
  *     Text_of_mail
  *     . (the final dot)
  *   QUIT
  * 
* For example: *
  *  ...
  *    try {
  *      SMTPmail mail = new SMTPmail("whateverhost.wherever.com");
  *      mail.setFrom("Bob Schulze", "bob@sleepers.com");
  *                                 // Brackets are not neccesary here
  *      mail.setTo("Whoever listens", "listener@taxoffice.gov");
  *                                 // Subsequent calls will append to the receivers' list
  *      mail.setSubject("Found something...");
  *      mail.addReplyTo("White House","president@whitehouse.gov")
  *      mail.addFile("/home/incometax.txt");
  *      mail.sendMail();
  *    } catch (SMTPException e) {
  *      System.out.println("Something went wrong with your income tax: " + e.getMessage());
  *    }
  * ...
  *
  * 
* * Check also: RFC822 (Message Format)
* * Check also: RFC821 (SMTP Protocol)
* * Check also: RFC1891..1894 (ESMTP Extensions)
* @version $Id: SMTPmail.java,v 1.3 1997/06/09 12:14:06 bob Exp bob $
*
  *                 $Log: SMTPmail.java,v $
  *                 Revision 1.3  1997/06/09 12:14:06  bob
  *                 addFile linefeed bug corrected.
  *
  *                 Revision 1.2  1997/06/06 12:28:24  bob
  *                 Initial Version:
  *                 - Standard Features for general purpose mailing
  *                 - Exceptions built in
  * 
* @author Bob Schulze */ public class SMTPmail { // global vars String smtp_host = "localhost" ; String s_from_name = "unknown"; String s_from_adr = "unknown@localhost"; String s_sender_name = ""; String s_sender_adr = ""; String s_subject = ""; String s_auth_name = "jSMTPmail"; // all targets are Vectors Vector s_to_name = new Vector(); // To Vector s_to_adr = new Vector(); Vector s_reply_name = new Vector(); // Reply-To Vector s_reply_adr = new Vector(); Vector s_cc_name = new Vector(); // CC Vector s_cc_adr = new Vector(); Vector s_bcc_name = new Vector(); // BCC Vector s_bcc_adr = new Vector(); // text StringBuffer s_text = new StringBuffer(); StringBuffer s_any = new StringBuffer(); // other Socket s = null; PrintStream pout; DataInputStream pin; // debug and verify boolean do_verify = true; boolean do_debug = false; int time_out = 20; //is time times 200 millies private void create_all_vars() { s_to_name.addElement("unknown"); s_to_adr.addElement("root@localhost"); s_reply_name.addElement("unknown"); s_reply_adr.addElement("root@localhost"); s_cc_name.addElement("unknown"); s_cc_adr.addElement("root@localhost"); s_bcc_name.addElement("unknown"); s_bcc_adr.addElement("root@localhost"); } private StringBuffer read_line ( DataInputStream fin ) throws IOException { // read one line get File in Stream StringBuffer line = new StringBuffer(""); char b; while (fin.available() > 0) { b = (char) fin.read(); if ( b == '\n' ) { // one line read break; } line.append(b); } return ( line); } private StringBuffer read_line_f ( FileInputStream fin ) throws IOException { // read one line get File in Stream StringBuffer line = new StringBuffer(""); char b; while (fin.available() > 0) { b = (char) fin.read(); if ( b == '\n' ) { // one line read break; } line.append(b); } return ( line); } /** * Creates a Mail Object that does the whole thing. * @param host The Host name known to the Runtime environment. Use getCodeBase() to find out. * @return The object */ public SMTPmail(String host) { smtp_host = host; create_all_vars(); } /** * Creates a Mail Object that does the whole thing. It will try to connect to the local host's smtpd. * @return The object */ public SMTPmail() { create_all_vars(); } /** * Sets the From Section. Subsequent calls will override. * @param real_name The real name of the User * @param address The address of the User * @return Nothing */ public void setFrom (String real_name, String address) { s_from_name = real_name ; s_from_adr = address; } /** * With verify == true, jSMTPmail will check each response from smtpd carefully. Strongly recommended. Otherwise, smtpd might miss some information and will finally fail delivering the mail. True by default. * @param how true or false * @return Nothing * @see #setTimeOut */ public void setVerify (boolean how) { do_verify = how ; } /** * With debug == true, jSMTPmail will check print out each handshake with smtpd. Useful for debugging or learning. Print out happens only when sendMail is activated. False by default. * @param how true or false * @return Nothing */ public void setDebug (boolean how) { do_debug = how ; } /** * Timeout sets the time, that jSMTPmail waits for the hosts response until it fires a Exception. Use a bigger value on slow hosts. Is set to 20 time 200 ms (4s) by default * @param steps time units of 200 ms, e.g. 20 means 4 seconds * @return Nothing */ public void setTimeOut (int steps) { time_out = steps ; } /** * Sets the To Section. Can be called multiple times in order to Send mail to varios recipients. * @param real_name The real name of the User * @param address The address of the User * @return Nothing */ public void addTo (String real_name, String address) { if ( ((String) s_to_name.elementAt(0)).compareTo("unknown") == 0 ) { // used first time - clean up array s_to_name.removeAllElements(); s_to_adr.removeAllElements(); } s_to_name.addElement(real_name) ; s_to_adr.addElement(address); } /** * Sets the CC Section. Can be called multiple times in order to Send mail to varios recipients. * @param real_name The real name of the User * @param address The address of the User * @return Nothing */ public void addCc (String real_name, String address) { if ( ((String) s_cc_name.elementAt(0)).compareTo("unknown") == 0 ) { // used first time - clean up array s_cc_name.removeAllElements(); s_cc_adr.removeAllElements(); } s_cc_name.addElement(real_name) ; s_cc_adr.addElement(address); } /** * Sets the BCC Section. Can be called multiple times in order to Send mail to varios recipients. * @param real_name The real name of the User * @param address The address of the User * @return Nothing */ public void addBcc (String real_name, String address) { if ( ((String) s_bcc_name.elementAt(0)).compareTo("unknown") == 0 ) { // used first time - clean up array s_bcc_name.removeAllElements(); s_bcc_adr.removeAllElements(); } s_bcc_name.addElement(real_name) ; s_bcc_adr.addElement(address); } /** * Sets the Reply-To Adresses. Can be called multiple times ?? * @param real_name The real name of the User * @param address The address of the User * @return Nothing */ public void addReplyTo (String real_name, String address) { if ( ((String) s_reply_name.elementAt(0)).compareTo("unknown") == 0 ) { // used first time - clean up array s_reply_name.removeAllElements(); s_reply_adr.removeAllElements(); } s_reply_name.addElement(real_name) ; s_reply_adr.addElement(address); } /** * Sets any additional field according to RFC822 like: *
    *    resent-date =  "Resent-Date" ":"   date-time
    *    "Resent-To"   ":" 1#address
    *    "Resent-cc"   ":" 1#address
    *    "Resent-bcc"  ":"  #address
    *    "Message-ID"        ":"   msg-id ( msg-id      =  "<" addr-spec ">" )
    *    "Resent-Message-ID" ":"   msg-id
    *    "In-Reply-To"       ":"   *(phrase / msg-id)
    *    "References"        ":"   *(phrase / msg-id)
    *    "Keywords"          ":"   #phrase
    *    "Subject"           ":"   *text
    *    "Comments"          ":"   *text
    *    "Encrypted"         ":"   1#2word
    *    extension-field           Head with "X-"
    * 
* check also: RFC822
* * Subsequent calls will override. Note that many email-Readers do ignore this part. * @param complete_line The complete line that will be sent. You are responsible for its correctness. * @return Nothing */ public void addAny (String complete_line) { s_any.append(complete_line + "\n"); } /** * Sets the Subject. will override when called multiple times. Reset it by calling it with an empty string. * @param subject The Subject line * @return Nothing */ public void setSubject( String subject ) { s_subject = subject ; } /** * Adds some contents to your mail. No formatting on it is done. * @param text Text to append to the mail. * @return Nothing */ public void addText (String text) { s_text.append(text) ; } /** * Adds the contents of file to your mail. No formatting on it is done (Also not linefeeds!) * @param text Text File to append to the mail. * @return Nothing */ public void addFile (String filename) throws SMTPException { File f = new File(filename); try { if ( f.exists() == true ) { FileInputStream fin = new FileInputStream(f); StringBuffer line = new StringBuffer(""); while (fin.available() > 0) { line = read_line_f(fin); addText(new String(line + "\n")); // was stripped at read line } } else { throw (new SMTPException("File Not found: " + filename)); } } catch (IOException e) { throw (new SMTPException("File " + filename + " read error: " + e.getMessage())); } } /** * Build the "My Name " String. Can be used to construct additional fields. * @param cleartext e.g. "My Name" * @param address e.g. "me@wherever.com" * @return "My Name " */ private String buildStandardField( String cleartext, String address) { return (cleartext + " <" + address + "> "); } /** * Checks, last response */ private void check_response (int respvalue) throws SMTPException { // first, lets wait few ms to let the smtp daemon react String response = ""; int counter = 0; while ( response.length() == 0 ) { try { Thread.sleep(500); } catch ( InterruptedException e1) { } try { response = new String(read_line(pin)); } catch ( IOException e ) { throw (new SMTPException("IOException while reading from smtpd.: " + e.getMessage())); } counter += 1; if (counter > time_out) { // stop it. theres somthing wrong throw (new SMTPException("SMTPmail: Timeout while waiting for smtpd response.")); } } if (do_debug == true) { System.out.println("-- SMTPmail Debug: Response: \n" + response ); } // check return value int retval = 0; try { retval = ((int) Integer.parseInt( response.substring(0,3)) ); } catch (Exception e) { retval = 0; } if ( retval == respvalue ) { if (do_debug == true) { System.out.println("-- SMTPmail Debug: Response Ok (" + retval + "). "); } } else { if (do_debug == true) { System.out.println("-- SMTPmail Debug: Response NOT Ok. (Should be " + response + " but is " + retval + "Troughd exception."); } // trou exception //throw (new SMTPException("Wrong smtpd response: Should be " + response + ", is " + // retval )); } } private void send_out ( String msg, int respvalue ) throws SMTPException { // read and write channel are global pout.println(msg); if (do_verify == true) { if (do_debug == true) { System.out.println("-- SMTPmail Debug: Sent: \n" + msg); } check_response( respvalue ); } } /** * Send the mail that was specified using the other methods * @exception SMTPException */ public void sendMail() throws SMTPException { try { int i; s = new Socket(smtp_host,25); pout = new PrintStream(s.getOutputStream(),true); pin = new DataInputStream(s.getInputStream()); check_response(220); // SMTP send_out("HELO " + s_auth_name,250); send_out("MAIL FROM: <" + s_from_adr + ">", 250); // -- target adressees for ( i = 0; i < s_to_adr.size() ; i++) { //to send_out("RCPT TO: <" + ((String) s_to_adr.elementAt(i)) + ">", 250); } if ( ((String) s_cc_name.elementAt(0)).compareTo("unknown") != 0 ) { for ( i = 0; i < s_cc_adr.size() ; i++) { //cc send_out("RCPT TO: <" + ((String) s_cc_adr.elementAt(i)) + ">", 250); } } if ( ((String) s_bcc_name.elementAt(0)).compareTo("unknown") != 0 ) { for ( i = 0; i < s_bcc_adr.size() ; i++) { //bcc send_out("RCPT TO: <" + ((String) s_bcc_adr.elementAt(i)) + ">", 250); } } // Data send_out("DATA", 354); if (do_debug == true) { System.out.println("-- SMTPmail Debug: Sending message body... "); } // -- std Fields pout.println("From: " + buildStandardField(s_from_name, s_from_adr)); // from pout.println("Sender: " + buildStandardField(s_sender_name, s_sender_adr)); // Sender for ( i = 0; i < s_to_adr.size() ; i++) { //to pout.println("To: " + buildStandardField(((String) s_to_name.elementAt(i)), ((String) s_to_adr.elementAt(i)) )); } if ( ((String) s_reply_name.elementAt(0)).compareTo("unknown") == 0 ) { //reply-to // not used } else { for ( i = 0; i < s_reply_adr.size() ; i++) { pout.println("Reply-To: " + buildStandardField(((String) s_reply_name.elementAt(i)), ((String) s_reply_adr.elementAt(i)) )); } } if ( ((String) s_cc_name.elementAt(0)).compareTo("unknown") == 0 ) { //cc // not used } else { for ( i = 0; i < s_cc_adr.size() ; i++) { pout.println("Cc: " + buildStandardField(((String) s_cc_name.elementAt(i)), ((String) s_cc_adr.elementAt(i)) )); } } // bcc's not - they are bliend pout.println("Subject: " + s_subject); // -- extraordinary data if ( s_any.length() > 0 ) { pout.println(s_any); } pout.println(""); // text pout.println(s_text + "\n"); // done pout.println("."); check_response(250); } catch (IOException e) { // through my own exception if ( do_debug == true) { System.out.println("-- SMTPmail Debug: Error" + e.getMessage() + " throw exception." ); } throw (new SMTPException("IOException collected: " + e.getMessage())); } try { // this has to be handles extra, because smtpd might be quick send_out("QUIT", 221); s.close(); } catch (IOException e1) { // ignore it here } if ( do_debug == true) { System.out.println("-- SMTPmail Debug: done."); } } }