2014年8月19日 星期二

JAVA POP3 Server 實作

※2014/09/01 JAVA POP3 Server 信件內文中斷符號問題修正

這篇可能有點長,主要是利用Java ServerSocket實作POP3 Server

當然免不了要先看一下什麼是 POP3 

接著就必須要瞭解 RFC 規範
基本上要看的就是 RFC1939RFC2449 這兩份文件

RFC1939
第三點 Basic Operation (基本操作),這是必看的,裡面有講那很多規定
例如:POP3 service by listening on TCP port 110.  Commands 不分大小寫. 參數長度不能超過40字元. 換行符號必須是CRLF.....等等

POP3 session 的三個狀態在基本操作也有說明
AUTHORIZATION State
TRANSACTION State
UPDATE State

接著就是必須實作的幾個指令
USER
PASS
STAT
LIST
RETR
DELE
NOOP
RSET
QUIT

POP Client Commands 的範例表


接下來就看程式吧!!

public class POP3Server extends Thread {
private static final Logger log = Logger.getLogger(POP3Server.class);
private static final int SERVER_PORT = 110;
private static final int MAX_CLIENT = 500;
private ServerSocket listenSocket = null;

public static void main(String[] args) {
POP3Server server = new POP3Server();
server.start();
}

public POP3Server() {
try {
listenSocket = new ServerSocket(SERVER_PORT, MAX_CLIENT);
} catch (IOException excpt) {
log.error("Sorry to open port " + SERVER_PORT + ":" + excpt);
System.exit(1);
}
}

public void run() {
Socket clientSocket = null;
try {
while (true) {
clientSocket = listenSocket.accept();
int numThreads = Thread.activeCount();
log.info(numThreads);
manageconnection newhandle = new manageconnection(clientSocket);
Thread newhandlethread = new Thread(newhandle);
newhandlethread.start();
log.info("hello");

}
} catch (IOException excpt) {
log.error("Sorry ,Failed I/O:" + excpt);
}
}
}

/* follow is manage and process command class */

class manageconnection implements Runnable {
private static final Logger log = Logger.getLogger(manageconnection.class);
private static final boolean AUTOFLUSH = true;
private Socket mySocket = null;
private PrintWriter out = null;
private BufferedReader in = null;
private long totalsize = 0;
private String username = null;
private List> mList;
private Set delMark = new HashSet();

public manageconnection(Socket newSocket) {
mySocket = newSocket;
}

public void run() {
String nextline = null;
int check = 0;
String command = null;
String arg1;
try {
Thread.currentThread();
Thread.sleep(10);
} catch (Exception e) {
log.error(e);
}
try {
mySocket.setTcpNoDelay(true);
} catch (SocketException excpt) {
log.error(excpt);
}
try {
System.setProperty("line.separator", "\r\n");
out = new PrintWriter(mySocket.getOutputStream(), AUTOFLUSH);
in = new BufferedReader(new InputStreamReader(
mySocket.getInputStream())

);
out.println("+OK" + " PoWei's pop3 server");
try {
while (true) {
if (check != 3) {
try {
nextline = in.readLine();
} catch (Exception excpt) {
log.error("sorry:" + excpt);
break;
}
log.info("C: " + nextline);

if (nextline.length() < 4) {
out.println("-ERR");
} else {
command = nextline.substring(0, 4);

switch (command.toUpperCase()) {
case "CAPA":
out.println("+OK Capability list follows");
out.println("USER");
out.println("PASS");
out.println("STAT");
out.println("QUIT");
out.println("LIST");
out.println("RETR");
out.println("DELE");
out.println("UIDL");
out.println("NOOP");
// out.println("TOP");
out.println(".");
break;
case "USER":
if (check == 0) {
check = 1;
if (nextline.length() < 5) {
out.println("-ERR");
} else {
arg1 = nextline.substring(5);
username = arg1;
out.println("+OK Password required for "
+ username);
}
} else {
out.println("-ERR");
}
break;
case "PASS":
if (check == 1) {
if (nextline.length() < 5) {
out.println("-ERR");
} else {
arg1 = nextline.substring(5);
if (check(username, arg1)) {
check = 2;
mList = readmail(username);
out.println("+OK " + username
+ " has " + mList.size()
+ " messages");
} else {
out.println("-ERR sorry auth failed");
check = 0;
}
}
} else {
out.println("-ERR");
}
break;
case "STAT":
if (check == 2) {
mList = readmail(username);
out.println("+OK" + " " + mList.size()
+ " " + totalsize);
} else {
out.println("-ERR");
}
break;
case "QUIT":
out.println("+OK BYE BYE welcome to PoWei's Mail System next time");
if (check == 2) {
if (!deleteMail(delMark)) {
out.println("-ERR some deleted messages not removed");
}
}
check = 3;
break;
case "LIST":
if (check == 2) {
out.print("+OK ");
out.println(mList.size() + " messages ("
+ totalsize + " byte)");
for (int j = 1; j <= mList.size(); j++) {
out.println(j + " "
+ mList.get(j - 1).get("size"));
}
out.println(".");
} else {
out.println("-ERR");
}
break;
case "RETR":
if (check == 2) {
if (nextline.length() < 5) {
out.println("-ERR");
} else {
arg1 = nextline.substring(5);
out.println("+OK");
log.info(arg1);
printMail(Integer.valueOf(arg1));
out.println(".");
boolean st = out.checkError();
log.info(st);
}
} else {
out.println("-ERR");
}
log.info("retr finished");
break;
case "DELE":
if (check == 2) {
if (nextline.length() < 5) {
out.println("-ERR");
} else {
arg1 = nextline.substring(5);
try {
Integer index = Integer
.valueOf(arg1) - 1;
if (!delMark.contains(index)) {
delMark.add(index);
out.println("+OK message "
+ arg1 + " deleted");
} else {
out.println("+ERR message "
+ arg1
+ " already deleted");
}

} catch (Exception e) {
out.println("-ERR");
}
}
} else {
out.println("-ERR");
}
break;
case "UIDL":
if (check == 2) {
if (nextline.length() < 5) {
out.println("+OK");
for (int j = 1; j <= mList.size(); j++) {
out.println(j
+ " "
+ mList.get(j - 1).get(
"uid"));
}
out.println(".");
} else {
Integer index = null;
try {
index = Integer.valueOf(nextline
.substring(5));
out.print("+OK ");
out.println(index
+ " "
+ mList.get(index - 1).get(
"uid"));
out.println(".");
} catch (IndexOutOfBoundsException e) {
out.println("-ERR IndexOutOfBoundsException:"
+ index);
} catch (NumberFormatException e) {
out.println("-ERR " + index);
}
}
} else {
out.println("-ERR");
}
break;
case "NOOP":
if (check == 2) {
out.println("+OK");
}
break;
case "TOP":
if (check == 2) {
out.println("+OK");
} else {
out.println("-ERR");
}
break;
default:
out.println("-ERR");
break;

}
}
} else {
out.close();
in.close();
mySocket.close();
break;
}

}
} catch (NullPointerException excpt) {
log.error("sorry " + excpt);
in.close();
out.close();
mySocket.close();
}

} catch (IOException excpt) {
log.error("Failed I/O:" + excpt);
}

log.info("bye");

}

private boolean deleteMail(Set delMark) {
boolean value = true;

for (Integer index : delMark) {
try {
Map map = mList.get(index);
if (map != null) {
//TODO remove mail
}

} catch (Exception e) {
value = false;
}
}

return value;
}

private void printMail(Integer index) {
String str = "";
GridFSDBFile gfsdbf = glist.get(index - 1);
InputStream inputS = null;
BufferedReader reader = null;
try {
inputS = gfsdbf.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputS));

while ((str = reader.readLine()) != null) {
out.println(str);
}

} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
} finally {
try {
if (inputS != null)
inputS.close();
if (reader != null)
reader.close();
} catch (Throwable ignore) {
}
}
}

/* check your username and password */
private boolean check(String username, String password) {
//TODO auth

return false;
}

private List> readmail(String uname) {
//TODO load mail

List> ret = new ArrayList>();

return ret;
}

}

沒有留言:

張貼留言