// POP3 class -- Williams
import java.io.*;
import java.net.*;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Enumeration;

public class PopClient {
    protected Socket skt;
    protected OutputStreamWriter out;
    protected BufferedReader in;
    public String response;
    // mailbox stats
    public int messageCount=0;
    public int totalSize=0;

    // Write a string with CRLF and check response
    protected boolean output(String s) throws IOException {
	out.write(s+"\r\n");
	out.flush();
	response=in.readLine();
	if (response.charAt(0)!='+') return false;
	return true;

    }

    // Connect, login, and set the status variables
    public boolean connect(String host,String user,String pw,int port)
     throws UnknownHostException, IOException  {
	if (port==0) port=110; // default port
	skt=new Socket(host,port);
	out=new OutputStreamWriter(skt.getOutputStream());
	in=new BufferedReader(new InputStreamReader(skt.getInputStream()));
	response=in.readLine();
	if (response.charAt(0)!='+') return false;
	if (!output("USER " + user)) return false;
	if (!output("PASS " + pw)) return false;
	if (!output("STAT")) return false;
	StringTokenizer tokens = new StringTokenizer(response);
	tokens.nextToken(); // throw away first
	messageCount=Integer.parseInt(tokens.nextToken());
	totalSize=Integer.parseInt(tokens.nextToken());
	return true;
    }

    // Do a quit with or without an RSET
    public boolean quit(boolean delete) throws IOException {
	boolean rv=true;
	if (!delete) 
	    if (!output("RSET")) rv=false;
	if (!output("QUIT")) rv=false;
	try {
	    skt.close();
	}
	catch (IOException e) {}
	return rv;
    }

    // Mark a message for deletion
    public boolean delete(int n) throws IOException {
	if (n>messageCount) return false; // catch overflow locally
	return output("DELE " + Integer.toString(n));
    }

    // Read all messages (could take a while!)
    public MailMessage[] readAll() throws IOException {
	MailMessage [] msgs = new MailMessage[messageCount];
	for (int i=0;i<messageCount;i++) {
	    msgs[i]=readMail(i+1);
	}
	return msgs;
    }

    // Read a specific message
    public MailMessage readMail(int n) throws IOException {
	if (n>messageCount) return null; // catch overflow locally
	MailMessage mail;
	Hashtable headers=new Hashtable();
	String line;
	StringBuffer bodytext=new StringBuffer();
	boolean body=false;
	if (!output("RETR " + Integer.toString(n))) return null;
	String lastHeader = "";
	// for each line of response...
	while (true) {
	    line=in.readLine();
	    if (line.length()==0 && !body) { 
		body=true; // blank line in headers means body
		continue;
	    }
	    else {
		// check for end
		if (line.length()!=0 && line.charAt(0)=='.' && line.length()==1) 
 			break; 
		// check for escaped period
		if (line.length()!=0 && line.charAt(0)=='.') 
   line=line.substring(1);
	    }
	    // if body, just build up lines
	    if (body) {
		bodytext.append(line);
		bodytext.append("\n");
	    }
	    else {  // headers
		// if a wrap-around header, append it
		if (line.charAt(0)==' ' || line.charAt(0)=='\t') {
		    headers.put(lastHeader,
				(String)headers.get(lastHeader)+" "+
				line.trim());
		    continue;
		}
		// otherwise split header line at :
		int ndx=line.indexOf(':');
		if (ndx!=-1) {
		    String key=line.substring(0,ndx).trim();
		    String value=line.substring(ndx+1).trim();
		    lastHeader=key.toLowerCase();
		    // and store in hashtable
		    headers.put(lastHeader,value);
		}
	    }
	}
	// ok we got it all I think
	mail=new MailMessage((String)headers.get("from"),
			     (String)headers.get("to"),
			     (String)headers.get("cc"),"",
			     (String)headers.get("subject"),
			     bodytext.toString());
	// remove "standard headers"
	headers.remove("from");
	headers.remove("to");
	headers.remove("cc");
	headers.remove("subject");
	mail.headers=headers;
	return mail;
    }

    // Test main
    public static void main(String args[]) throws Exception {
	PopClient pop= new PopClient();
	if (pop.connect(args[0],args[1],args[2],0)) {
	    System.out.println(pop.messageCount + " messages");
	    if (pop.messageCount!=0) {
		MailMessage [] msgs=pop.readAll();
		for (int i=0;i<msgs.length;i++) {
		    MailMessage msg=msgs[i];
		    System.out.println(msg.body);
		    System.out.println("----- Headers -----");
		    Enumeration keys=msg.headers.keys();
		    while (keys.hasMoreElements()) {
			String iKey=(String)keys.nextElement();
			System.out.println(iKey + ":" + msg.headers.get(iKey));
		    }
		    System.out.println("****** NEXT MESSAGE ******");
		}
	    }
	}
	else {
	    System.out.println("Error : " + pop.response);
	}
	pop.quit(false);
    }
}
