/*
originaly written before 2000
resurected for 2004
rednuht

simple fake single threaded pop3 server, uses file / direcory and accepts any user/password

as with most of my source code this is released under the !GNU licencing system, you should not have recived a copy in this distribution archive.

feel free to hack this to your needs.
new information may or may not be available at 
http://www.jumpstation.co.uk/javapopper/
*/
import java.net.*;
import java.util.*;
import java.io.*;

class telpopserver {

	static PrintStream ps;
	static DataInputStream dis;
	static String cmdUser="user";
	static String cmdPass="pass";
	static String cmdList="list";
	static String cmdRetr="retr";
	static String cmdStat="stat";
	static String cmdRset="rset";
	static String cmdDele="dele";

	static String dropzone="/home/rednuht/projects/popper/dropzone/.";
	static String deadzone="/home/rednuht/projects/popper/deleted/.";

	static int totalNumberOfMessages;
	static long totalSizeOfWaitingMessages;
	static Vector messages = new Vector();

	public static void main(String[] args) {
		Socket s = null;
		loadMessages();
		int messageNo;
		String reply;
		if (1==2) {
			//shutdownPhase();
		} else {
		//while(true) {
		try {
			System.out.println("POPPER..");
			ServerSocket serverConnect = new ServerSocket(110);
			System.out.println("..");
			System.out.println("server created on " + serverConnect.getLocalPort());
			s = serverConnect.accept();
			ps = new PrintStream(s.getOutputStream());
			dis = new DataInputStream(s.getInputStream());
			sendMsg("+OK Welcome to POPPER v1.02");
			reply = getMsg();
			String mode ="AUTH1";

//	String mode ="TRANS";
//	reply = "retr 4";
			while (reply.equals("quit") == false) {
				if (reply.startsWith(cmdUser)&& mode.equals("AUTH1")) {
					if (reply.length() > 1+cmdUser.length()) {
						sendMsg("+OK Password required for "+reply.substring(1+cmdUser.length(),reply.length()));
						mode="AUTH2";
					} else {
						sendMsg("-ERR Expected user name");
					}
				} else if (reply.startsWith(cmdPass)&& mode.equals("AUTH2")) {
					if (reply.length() > 1+cmdPass.length()) {
						sendMsg("+OK password accepted "+reply.substring(1+cmdPass.length(),reply.length()));
						loadMessages();
						mode="TRANS";
					} else {
						sendMsg("-ERR Password Supplied is incorrect");
					}
				// login phase complete (AUTH1/AUTH2)
				} else if(reply.startsWith(cmdStat)&& mode.equals("TRANS")) {
					sendMsg("+OK " + totalNumberOfMessages + " " + totalSizeOfWaitingMessages);
				} else if(reply.startsWith(cmdRset)&& mode.equals("TRANS")) {
					sendMsg("+OK But not supported and we are not telling you");
				} else if(reply.startsWith(cmdDele)&& mode.equals("TRANS")) {
					// get message number
					messageNo = Integer.parseInt(reply.substring(1+cmdDele.length(),reply.length()),10);
					// delete file - set unread status to false
					messageFile mess = (messageFile)messages.elementAt(messageNo-1);
					mess.unread = false;
					sendMsg("+OK Message " + messageNo + " deleted");
				} else if (reply.startsWith(cmdList)&& mode.equals("TRANS")) {
					// list all messages by order and size
					for (int i = 0; i < totalNumberOfMessages;i++) {
						messageFile mess = (messageFile)messages.elementAt(i);
						if (mess.unread==true) {
							sendMsg("+OK " + (1+i) + " " + mess.size); // 0 base/d vector array
						}
					System.out.println(totalNumberOfMessages + " messages to list, listing message " + i);
					}
					// send dot
					sendMsg(".");
				} else if(reply.startsWith(cmdRetr)&& mode.equals("TRANS")) {
					String fn;
					messageNo = Integer.parseInt(reply.substring(1+cmdRetr.length(),reply.length()),10);
					// get message number
					sendMsg("+OK Message " + messageNo + " follows");
					messageFile mess = (messageFile)messages.elementAt(messageNo-1);
					fn = mess.name;
					// open file
					try {
						File N_dataFile = new File(dropzone,fn);
						FileInputStream I_dataFile = new FileInputStream(N_dataFile);
						BufferedReader  b = new BufferedReader(new InputStreamReader(I_dataFile));
						String dl = b.readLine();
						while (dl!=null) {
							sendMsg(dl);
							// get next line of data
							dl = b.readLine();
							// follow rfc byte stuffing rules
							if (dl!=null) {	// otherwise the next line gets compared with null
								if (dl.startsWith(".")) { dl = "." + dl; }
							}
						}
						// close file
						I_dataFile.close();
					} catch (FileNotFoundException e) {
							System.err.println("file not found " + dropzone + fn);
	            	} catch (IOException e) {
    	        		System.err.println("Reading data file :"+ e);
       				}
					// at eof
					// send blank line
					sendMsg("");
					// send dot
					sendMsg(".");
				} else {
					sendMsg("-ERR Unknown command [" + reply + "] in mode [" + mode + "]");
				}
				reply = getMsg();
			}
			sendMsg("+OK POPPER signing off");
			ps.flush();
			s.close();
			shutdownPhase();

		} catch (IOException e) {
			System.out.println(e);
		}
		//} // end of infinite loop
		} // dodgy if
	}

	// delete read messages (or just move them to another folder
	public static void shutdownPhase() {
		// for each read message delete the file (or move it elsewhere)
		boolean status;
		for (int i = 0; i < totalNumberOfMessages;i++) {
			messageFile mess = (messageFile)messages.elementAt(i);
			if (mess.unread==false) {
				System.out.print("Backing up [" + mess.name);
				File origFile = new File(dropzone,mess.name);
				File destFile = new File(deadzone,mess.name);
				status = origFile.renameTo(destFile);
				System.out.println("]  [" + status + "]");
			}
		}
	}

	// load message details into vector array
	public static void loadMessages() {
		long fs;
		totalNumberOfMessages = 0;
		totalSizeOfWaitingMessages = 0;
		// get directory
		File cdir = new File(dropzone);
//		File cdir = new File(".");
		String clist[] = cdir.list();
		// for some reason Outlook express ignores the first message
		messages.addElement(new messageFile("nonexistant.message",0));
		totalNumberOfMessages++;
		// get file name and size
		for (int i = 0; i < clist.length; i++) {
			File ccurrent = new File(dropzone,clist[i]); // note the direcory ! if this is not here the clist item does not have enough info to find the real file unless you run from the directory !
			fs=ccurrent.length();
			System.out.println(clist[i] + " " + fs + " bytes");
			// add to vector
			messages.addElement(new messageFile(clist[i],fs));
			totalNumberOfMessages++;
			totalSizeOfWaitingMessages+=fs;
		}
		System.out.println("Total files found " + totalNumberOfMessages + " with a combinted size of " + totalSizeOfWaitingMessages + " bytes");
	}

	public static void sendMsg(String sMsg) {
		ps.print(sMsg + "\r\n");
		ps.flush();
		System.out.println("Tx->["+sMsg+"]->");
	}

	public static String getMsg() {
		String rMsg;
		try {
			rMsg = dis.readLine();
		} catch (IOException e) {
			System.out.println(e);
			rMsg="ERROR - No Data Read";
		}
		if (rMsg==null) {
			rMsg="quit";
		} else {
			System.out.println("Rx<-["+rMsg+"]<-");
		}
		return(rMsg.toLowerCase());
	}


} // End of POPPER class definition

// data class
class messageFile
{
	long size;
	String name;
	boolean unread;

	public messageFile(String fn, long fs)
	{
		name = fn;
		size =fs;
		unread=true;
	}

} // end of data class
