/**
 * Generic SMTP class
 * Written by R Rawson-Tetley, 2005
 *
 * Consider this class public domain
 */

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

public class SMTP {

    static final int DEFAULT_PORT = 25;
    static final String EOL = "\r\n"; // network end of line

    protected DataInputStream reply = null;
    protected PrintStream send = null;
    protected Socket sock = null;

    
    /**
     *   Create an object pointing to the specified host
     *   @param hostid The host to connect to.
     *   @exception UnknownHostException
     *   @exception IOException
     */
    public SMTP(String hostname) throws UnknownHostException, IOException {
        this(hostid, DEFAULT_PORT);
    }

    public SMTP(String hostname, int port) throws UnknownHostException, IOException {
        sock = new Socket( hostid, port );
        reply = new DataInputStream( sock.getInputStream() );
        send = new PrintStream( sock.getOutputStream() );
        String rstr = readLine(reply);
        if (!rstr.startsWith("220")) throw new ProtocolException(rstr);
        while (rstr.indexOf('-') == 3) {
            rstr = readLine(reply);
            if (!rstr.startsWith("220")) throw new ProtocolException(rstr);
        }
    }

    public SMTP( InetAddress address ) throws IOException {
        this(address, DEFAULT_PORT);
    }

    public SMTP( InetAddress address, int port ) throws IOException {
        sock = new Socket( address, port );
        reply = new DataInputStream( sock.getInputStream() );
        send = new PrintStream( sock.getOutputStream() );
        String rstr = readLine(reply);
        if (!rstr.startsWith("220")) throw new ProtocolException(rstr);
        while (rstr.indexOf('-') == 3) {
            rstr = readLine(reply);
            if (!rstr.startsWith("220")) throw new ProtocolException(rstr);
        }
    }

    public void sendmsg( String from_address,
		         String to_address,
                         String subject, String message, 
                         )
                         throws IOException, ProtocolException {

        String rstr;
        String sstr;

        InetAddress local;
        try {
          local = InetAddress.getLocalHost();
        }
        catch (UnknownHostException ioe) {
          System.err.println("No local IP address found - is your network up?");
          throw ioe;
        }
        String host = local.getHostName();
        send.print("HELO " + host);
        send.print(EOL);
        send.flush();
        rstr = readLine(reply);
        if (!rstr.startsWith("250")) throw new ProtocolException(rstr);
        sstr = "MAIL FROM: " + from_address ;
        send.print(sstr);
        send.print(EOL);
        send.flush();
        rstr = readLine(reply);
        if (!rstr.startsWith("250")) throw new ProtocolException(rstr);
        sstr = "RCPT TO: " + to_address;
        send.print(sstr);
        send.print(EOL);
        send.flush();
        rstr = readLine(reply);
        if (!rstr.startsWith("250")) throw new ProtocolException(rstr);
        send.print("DATA");
        send.print(EOL);
        send.flush();
        rstr = readLine(reply);
        if (!rstr.startsWith("354")) throw new ProtocolException(rstr);
        send.print("From: " + from_address);
        send.print(EOL);
        send.print("To: " + to_address);
        send.print(EOL);
        send.print("Subject: " + subject);
        send.print(EOL);

        // Create Date - we'll cheat by assuming that local clock is right
        Calendar today_date = Calendar.getInstance();
        send.print("Date: " + msgDateFormat(today_date));
        send.print(EOL);
        send.flush();

        // Warn the world that we are on the loose - with the comments header:
        // send.print("Comment: Unauthenticated sender");
        // send.print(EOL);
        send.print("X-Mailer: Generic SMTP");
        send.print(EOL);

        // Sending a blank line ends the header part.
        send.print(EOL);

        // Now send the message proper
        send.print(message);
        send.print(EOL);
        send.print(".");
        send.print(EOL);
        send.flush();

        rstr = readLine(reply);
        if (!rstr.startsWith("250")) throw new ProtocolException(rstr);
    }
        
    public void close() {
      try {
        send.print("QUIT");
        send.print(EOL);
        send.flush();
        sock.close();
      }
      catch (IOException ioe) {
        // As though there's anything I can do about it now...
      }
    }

    protected void finalize() throws Throwable {
        this.close();
        super.finalize();
    }

    private String msgDateFormat(Calendar senddate) {
        String formatted = "hold";

        String Day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
        String Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

        formatted = Day[senddate.get(Calendar.DAY_OF_WEEK)] + ", ";
        formatted = formatted + Integer.toString(senddate.get(Calendar.DAY_OF_MONTH)) + " ";
        formatted = formatted + Month[senddate.get(Calendar.MONTH)] + " ";
        formatted = formatted + Integer.toString(senddate.get(Calendar.YEAR)) + " ";
        if (senddate.get(Calendar.HOUR_OF_DAY) < 10) formatted = formatted + "0";
        formatted = formatted + Integer.toString(senddate.get(Calendar.HOUR_OF_DAY)) + ":";
        if (senddate.get(Calendar.MINUTE) < 10) formatted = formatted + "0";
        formatted = formatted + Integer.toString(senddate.get(Calendar.MINUTE)) + ":";
        if (senddate.get(Calendar.SECOND) < 10) formatted = formatted + "0";
        formatted = formatted + Integer.toString(senddate.get(Calendar.SECOND)) + " ";
        if (senddate.getTimeZone().getRawOffset() < 0)
            formatted = formatted + "+";
        else
            formatted = formatted + "-";
        if (Math.abs(senddate.getTimeZone().getRawOffset())/60 < 10) formatted = formatted + "0";
        formatted = formatted + String.valueOf(Math.abs(senddate.getTimeZone().getRawOffset())/60);
        if (Math.abs(senddate.getTimeZone().getRawOffset())%60 < 10) formatted = formatted + "0";
        formatted = formatted + String.valueOf(Math.abs(senddate.getTimeZone().getRawOffset())%60);

        return formatted;
    }
    
    /**
     * Replacement for deprecated DataInputStream.readLine()
     */
    private String readLine(DataInputStream reply) throws IOException {
        byte[] buff = new byte[1024];
        int bytesread = reply.read(buff);
        return new String(buff).substring(0, bytesread);
    }

}
