/*
 * IPInspector
 *
 * Copyright (C) 2002, Jonathan Sevy <jsevy@mcs.drexel.edu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
 
 /* class POPWeasel

Defines object that will communicate with POP and SMTP
servers to get and send e-mail (pardon the name)


Defines methods:

	public Vector getMessages(UserInfo user, String mailHost)
Retrieves messages from specified POP host, using supplied user ID and password.
Return as vector of Strings (one per message)

	public void sendMessages(UserInfo user, Vector messages, String mailHost)
Send the messages corresponding to the Vector supplied of Message objects,
using the specified SMTP host
	

*/


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



public class POPWeasel
{
	
	public static final int POPPORT = 110;
	public static final int SMTPPORT = 25;
	
	public static final boolean debug = false;
	
	
	
	public POPWeasel()
	{
	
	}
	
	public Vector getMessages(String userName, String password, String mailHost)
	    throws Exception
	{
		Socket mailSocket;
		BufferedReader istream;
		BufferedWriter ostream;
		Vector messageList = new Vector();
		
		
		mailSocket = new Socket(mailHost, POPWeasel.POPPORT);
		istream = new BufferedReader(new InputStreamReader(mailSocket.getInputStream()));
		ostream = new BufferedWriter(new OutputStreamWriter(mailSocket.getOutputStream()));
		
		establishPOPSession(userName, password, istream, ostream);
		messageList = retrieveMessages(istream, ostream);
		closePOPSession(istream, ostream);
		
		istream.close();
		ostream.close();
		mailSocket.close();
		
		
		return messageList;
		
	}
	
	
	
	
	public void sendMessages(String userEmailAddress, String mailHost, Vector messages)
	    throws Exception
	{
		Socket mailSocket;
		BufferedReader istream;
		BufferedWriter ostream;
		
		
		mailSocket = new Socket(mailHost, POPWeasel.SMTPPORT);
		istream = new BufferedReader(new InputStreamReader(mailSocket.getInputStream()));
		ostream = new BufferedWriter(new OutputStreamWriter(mailSocket.getOutputStream()));
		
		establishSMTPSession(mailHost, istream, ostream);
		sendMessages(userEmailAddress, messages, istream, ostream);
		closeSMTPSession(istream, ostream);
		
		istream.close();
		ostream.close();
		mailSocket.close();
			
		
		return;
		
	}
	
	
	// Utility method for sending a single email message
	public void sendMessage(String userEmailAddress, String mailHost, EmailMessage message)
	    throws Exception
	{
	    Vector messageVector = new Vector();
	    messageVector.add(message);
	    this.sendMessages(userEmailAddress, mailHost, messageVector);
	}
	
	
	
	private void checkPOPResponse(String response)
			throws IOException
	// utility method, to see if POP server sends happy response; throws a "fit"
	// (also known as an Exception) if response doesn't begin with "+OK"
	{
		StringTokenizer st;
		
		debugPrintln("Server: " + response);
		
		st = new StringTokenizer(response);
		if (!st.nextToken().equals("+OK"))
			throw new IOException(response);
	}
	
	
	
	private void establishPOPSession(String userName, String password, BufferedReader istream, BufferedWriter ostream)
			throws IOException
	// Send username and password to establish POP session
	{
		checkPOPResponse(istream.readLine());
		
		ostream.write("USER " + userName);
		ostream.newLine();
		ostream.flush();
		debugPrintln("Client: USER " + userName);
		checkPOPResponse(istream.readLine());
		
		ostream.write("PASS " + password);
		ostream.newLine();
		ostream.flush();
		debugPrintln("Client: PASS (hidden)");
		checkPOPResponse(istream.readLine());
		
		return;
	}
	
	
	
	
	private Vector retrieveMessages(BufferedReader istream, BufferedWriter ostream)
			throws IOException, NumberFormatException
	// Get waiting messages, sending STAT and RETR messages to POP host
	{
		Vector retrievedMessages = new Vector();
		StringTokenizer st;
		String nextLine;
		String nextToken;
		int numMessages;
		
		ostream.write("STAT");
		ostream.newLine();
		ostream.flush();
		debugPrintln("Client: STAT");
		
		nextLine = istream.readLine();
		debugPrintln("Server: " + nextLine);
		st = new StringTokenizer(nextLine);
		if (!st.nextToken().equals("+OK"))
			throw new IOException(nextLine);
		nextToken = st.nextToken();	// will be number of messages to retrieve
		numMessages = Integer.parseInt(nextToken);
		
		for (int i=1; i <= numMessages; ++i)
		{
			// get waiting messages
			
			String newMessage = new String();
			
			ostream.write("RETR " + i) ;
			ostream.newLine();
			ostream.flush();
			debugPrintln("Client: RETR " + i);
			
			checkPOPResponse(istream.readLine());
			
			while (!(nextLine = istream.readLine()).equals("."))
			{
				// add each line of message to message string
				newMessage += nextLine + "\n";
			}
			
			// add message to vector to be returned
			retrievedMessages.insertElementAt(newMessage, retrievedMessages.size());
		}
		
		
		return retrievedMessages;
		
	}
	
	
	
	
	private void closePOPSession(BufferedReader istream, BufferedWriter ostream)
			throws IOException
	{
		ostream.write("QUIT");
		ostream.newLine();
		ostream.flush();
		
	}
	
	
	
	
	private void checkSMTPResponse(String response, String expected)
			throws IOException
	// See if SMTP host returns string expected at current stage of communication;
	// if not, throw a fit (exception)
	{
		StringTokenizer st;
		
		debugPrintln("Server: " + response);
		
		st = new StringTokenizer(response);
		if (!st.nextToken().equals(expected))
			throw new IOException(response);
	}
	
	
	
	
	private void establishSMTPSession(String domainName, BufferedReader istream, BufferedWriter ostream)
			throws IOException
	{
		checkSMTPResponse(istream.readLine(), "220");
		
		ostream.write("HELO " + domainName);
		//ostream.newLine();
		ostream.write("\r\n");
		ostream.flush();
		debugPrintln("Client: HELO " + domainName);
		checkSMTPResponse(istream.readLine(), "250");
		
		return;
	}
			
	
	
	
	
	private void sendMessages(String userEmailAddress, Vector messages, BufferedReader istream, BufferedWriter ostream)
			throws IOException
	{
		Enumeration e = messages.elements();
		
		while (e.hasMoreElements())
		{
			// send next message
			EmailMessage m = (EmailMessage)e.nextElement();
			
			//try
			//{
				ostream.write("MAIL FROM:" + "<" + userEmailAddress + ">");
				//ostream.newLine();
				ostream.write("\r\n");
		        ostream.flush();
				debugPrintln("Client: MAIL FROM:" + "<" + userEmailAddress + ">");
				checkSMTPResponse(istream.readLine(), "250");
				
				Enumeration addresses = (m.recipients).elements();
				while(addresses.hasMoreElements())
				{
					String recipient = (String)addresses.nextElement();
					ostream.write("RCPT TO:" + "<" + recipient + ">");
					//ostream.newLine();
				    ostream.write("\r\n");
		            ostream.flush();
					debugPrintln("Client: RCPT TO:" + "<" + recipient + ">");
					checkSMTPResponse(istream.readLine(), "250");
				}
				
				ostream.write("DATA");
				//ostream.newLine();
				ostream.write("\r\n");
		        ostream.flush();
				debugPrintln("Client: DATA");
				checkSMTPResponse(istream.readLine(), "354");
				
				ostream.write(m.text);
				//ostream.newLine();
				ostream.write("\r\n");
		        //ostream.flush();
				debugPrintln("Message text sent");
				ostream.write(".");     // CR-LF  = 0x0D 0x0A = octal 015 012
				//ostream.newLine();
				ostream.write("\r\n");
		        //ostream.newLine();
				ostream.flush();
				//debugPrintln(".");
				checkSMTPResponse(istream.readLine(), "250");
			//}
			//catch (Exception except)
			//{
			//	debugPrintln("Problem sending email message: " + except);
			//}
			
		}
		
		return;
	}
	
	
	
	
	private void closeSMTPSession(BufferedReader istream, BufferedWriter ostream)
			throws IOException
	{
		ostream.write("QUIT");
		//ostream.newLine();
		ostream.write("\r\n");
		ostream.flush();
		debugPrintln("Client: QUIT");
	}
	
	
	
	private void debugPrintln(String message)
	{
	    if (this.debug)
	        System.out.println(message);
	}
			
	
	
	
	
	
}