/*
 * COPYRIGHT (c) 1998-1999 Jan-Henrik Haukeland, http://www.tildeslash.com/
 */

package ts.mail;

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

import ts.mail.MailModel;
import ts.biff.Util;


/** 
 * This class extends the abstract Mailserver and implements the
 * TransportAgent interface. The class (sub)implements the SMTP 
 * protocol for sending a mail message.
 *
 * @see AbstractMailServer
 * @see TransportAgent
 * @see MailModel
 *
 * @author  Jan-Henrik Haukeland &lt;hauk@tildeslash.com&gt;
 * @version $Id: SmtpAgent.java,v 1.10 1999/11/28 01:27:10 hauk Exp $ 
*/
public class SmtpAgent extends AbstractMailServer implements TransportAgent
{

  /** Default portnumber for the SMTP protocol */
  protected static final int DEFAULT_PORT= 25;


  /** Mail Transport Protocol used by this class */
  protected final static String PROTOCOL= "SMTP";

  /** Flag for safe termination */
  private boolean canTerminate= true;


  // ----------------------- Constructors ----------------------- //

  /**
   * Initializes the object using localhost and default portnumber
   *
   * @exception UnknownHostException  Is thrown if no IP address 
   *                                  for the localhost could be found
   */
  public SmtpAgent() throws UnknownHostException
  {
    super("localhost",DEFAULT_PORT);
  }


  /**
   * Initializes the object with the given values for the mail server
   *
   * @param aServer The MTA server address
   * @param aPort   The MTA server port number
   *
   * @exception UnknownHostException  Is thrown if no IP address 
   *                                  for the host could be found
   */
  public SmtpAgent(String aServer, int aPort) throws UnknownHostException
  {
    super(aServer,aPort);
  }


  /**
   * Implementation of the SafeTerminate interface
   * @return true if it's safe to terminate
   */
  public boolean canTerminate()
  {
    return canTerminate;
  }



  // ------------- Implementation of interface methods ----------------- //

  
  /** 
   * Implementation of sendMail method. This method connects to the
   * mail server's sendmail port and tries to send an ordinary SMTP
   * message
   *
   * @exception SendMailException Is thrown if the mail for some reason
   *                              could not be sent 
   */
  public synchronized void sendMail(MailModel aMail) throws SendMailException
  {

    // Validate the mail message
    try {
      aMail.validate();
    } catch (IllegalStateException e) {
      throw new SendMailException(SendMailException.ERROR,
				  "Got error validating the messsage: "+
				  e.getMessage(), aMail);
    }
    
    PrintWriter out=   null; // Sockets output stream
    BufferedReader in= null; // Sockets input stream
    
    try {
      
      // Don't let the application terminate when sending mail
      canTerminate= false;

      super.openConnection();
      
      out = new 
	  PrintWriter(super.getOutputStream(), true);
      
      in = 
	  new BufferedReader(new InputStreamReader(super.getInputStream()));
      

      // The SMTP sub-protocol
      out.println("HELO "+ InetAddress.getLocalHost().getHostName());
      isOk(in.readLine());
      out.println("MAIL FROM: "+aMail.getFromField());
      isOk(in.readLine());
      out.println("RCPT TO: "+aMail.getToField());
      isOk(in.readLine());

      // Send the mail to cc addresses also, if any
      Object[] cc= getCCHeaders(aMail);
      if ( cc != null ) {
	for (int i= 0; i<cc.length; i++) {
	  out.println("RCPT TO: "+(String)cc[i]);
	  isOk(in.readLine());
	}
      }
      
      out.println("DATA");
      isOk(in.readLine());
      out.println("To: "+aMail.getToField());
      out.println("Subject: "+aMail.getSubjectField());
      out.println(getOptionalHeaders(aMail));
      out.println(aMail.getMailBody());
      out.println(".");
      isOk(in.readLine());
    } catch (Exception e) {
      throw new SendMailException(SendMailException.ERROR,
				  e.getMessage());
    }
    finally {
      out.println("QUIT");
    
      // Now it's ok to terminate
      canTerminate= true;

      try {
	out.close();
	in.close();
	closeConnection();
      } catch (Exception e) {}
    }
  }


  /**
   * Implementation of super class abstract method. 
   * @return The protocol used
   */
  public String getProtocol() 
  {
    return PROTOCOL;
  }


  
  // ---------------------- private methods ---------------------- //


  /**
   * Check respons from sendmail server. This method has no granularity
   * on error checked. Only fatal error from server results in an exception
   * thrown.
   *
   * @exception SendMailExcpetion Is thrown if error status from server
   */
  private void isOk(String aStat) throws SendMailException
  { 
    int    stat= Integer.parseInt(aStat.substring(0,3));
    String msg=  aStat.substring(3);
    
    if ( stat >= 400 ) {
      throw new SendMailException(SendMailException.ERROR,
				  "Sendmail error; "+msg);
    }
  }



  /**
   * Returns an array with Carbon-copy addresses
   * @param m a MailModel object
   * @return an array of cc addresses if any
   */
  private Object[] getCCHeaders(MailModel m)
  {
    // Get CC-headers
    if ( m.hasCCHeaders() ) {

      Vector cc= new Vector();
      
      for (Enumeration e= m.getCCHeaders(); e.hasMoreElements();) {
	cc.addElement(e.nextElement());
      }
      return Util.convertToArray(cc);
    }

    return null;
  }    



    

  /**
   * Returns a string with optional headers
   * @param m a MailModel object
   * @return A string with optional headers from the MailModel
   */
  private String getOptionalHeaders(MailModel m)
  { 
    StringBuffer headers= new StringBuffer();
    
    // Get CC-headers
    if ( m.hasCCHeaders() ) {
      StringBuffer aCClist= new StringBuffer("Cc: ");
      Enumeration  e; 
      int          i;
      for (e= m.getCCHeaders(), i= 0; e.hasMoreElements(); i++) {
	String value= (String)e.nextElement();
	if (i==0)
	    aCClist.append(value);
	else
	    aCClist.append("," + value);
      }
      
      headers.append(aCClist + "\n");
    }
    
    // Get X-headers
    if ( m.hasXHeaders() ) {
      for (Enumeration e = m.getXHeaderKeys(); e.hasMoreElements();) {
	String key= (String)e.nextElement();
	headers.append(key + " " + m.getXHeaderValue(key) + "\n");
      }
    }

    // Get Reply-to header
    if ( ! m.getReplyToField().equals(MailModel.NO_FIELD_VALUE) )
	headers.append("Reply-to: "+m.getReplyToField() +"\n");
    
    // Get Organization header
    if ( ! m.getOrganizationField().equals(MailModel.NO_FIELD_VALUE) )
	headers.append("Organization: "+m.getOrganizationField() +"\n");

    return headers.toString();
  }

}

  
