2.2.2 .Cỏc đơn thể của mailserver
2.2.2.2. Xõy dựng POP3 Server
Trỡnh POP3 Server gồm hai phần, phần POPServer chịu trỏch nhiệm mở socket và lắng nghe kết nối từ trỡnh khỏch gởi lờn theo cổng 110 (cổng mặc định của giao thức POP3). Khi nhận được kết nối, POPServer yờu cầu lớp PễPCnnection phõn tớch cỏc lệnh của giao thức POP3 và gởi mail về cho trỡnh khỏch. Lớp POP3 được cài đặt như sau:
POPServer.java
import java.io.*; import java.net.*;
import java.util.*;
public class POPServer extends Thread { public Vector connections;
public Boolean stopRequested; protected ServerSocket listenSocket; public POPServer() throws Exception {
int port = 0;
String portString = null;
// Lấy sú hiệu cổng từ file cấu hỡnh try {
portString = Server.properties.getProperty("pop.port"); port = Integer.parseInt(portString);
} catch (NumberFormatException e) {
throw new Exception("Invalid 'pop.port' ư " + portString); }
// Lắng nghe trờn socket sự kết nối từ trỡnh khỏch listenSocket = new ServerSocket(port); // Tạo mảng chứa danh sỏch cỏc kết nối connections = new Vector(10, 10);
}
public void removeConnection(POPConnection connection) { }
/**
* Xử lý kết nối */
public void run() {
// Lặp vụ tận cho đến khi cú yờu cầu dừng stopRequested = new Boolean(false); while (true) { synchronized (stopRequested) { if (stopRequested.booleanValue()) break; } // Chấp nhận kết nối do trỡnh khỏch gởi lờn try { Socket s = listenSocket.accept();
POPConnection connection = new POPConnection(s, this); connections.addElement(connection); connection.setDaemon(true); connection.start(); } catch (IOException e) { e.printStackTrace(); break; } } // Đúng kết nối try { listenSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
Lớp POPConnection.java chịu trỏch nhiệm chớnh trong việc xử lý cỏc lệnh của giao thức POP3. Lớp POPConnection được cài đặt như sau :
POPConnection.java
import java.io.*; import java.net.*; import java.util.*;
public class POPConnection extends Thread { protected static final int ENTER_USER = 0;
protected static final int ENTER_PASSWORD = 1; protected static final int TRANSACTION = 2; protected static final int UPDATE = 3;
protected POPServer server; protected Socket socket; protected BufferedReader in; protected PrintStream out;
protected boolean stopRequested; protected int state;
protected String userName; protected User user;
protected Vector messages = null;
super();
this.socket = socket; this.server = server; }
protected void close() { try {
socket.close(); } catch (Exception e) {
System.err.println("Exception trying to close POPConnection socket!"); e.printStackTrace(System.err);
} }
// Đếm số mail cú trong hộp thư protected int countMessages() {
if (messages == null) return 0;
Enumeration enum = messages.elements(); int count = 0;
Message message = (Message)enum.nextElement(); if (!message.isDeleted()) count++; } return count; } /**
* Lấy nội dung mail dựa vào số thứ tự của mail lưu trong hộp thư */
protected Message getMessage(int number) { if (number <= 0 || number > messages.size())
return null;
Message message = (Message)messages.elementAt(number ư 1); if (message.isDeleted())
return null; return message; }
protected Message getMessage(String messageNumber) { int number;
number = Integer.parseInt(messageNumber); } catch (NumberFormatException e) { return null; } return getMessage(number); }
protected long getMessagesSize() { if (messages == null)
return 0;
Enumeration enum = messages.elements(); long size = 0;
while (enum.hasMoreElements()) {
Message message = (Message)enum.nextElement(); if (!message.isDeleted())
size += message.getSize(); }
return size; }
protected void processDELE(StringTokenizer arguments) { if (!arguments.hasMoreTokens()) {
out.println("ưERR must supply message number"); } else {
Message message = getMessage(arguments.nextToken()); if (message == null) {
out.println("ưERR no such message"); } message.setDeleted(true); out.println("+OK"); } } /**
* Xử lý lệnh PASS kiểm tra password */
protected void processEnterPassword(String command, StringTokenizer arguments) {
if (command.equalsIgnoreCase("QUIT")) { stopRequested = true;
out.println("+OK Signing off");
} else if (command.equalsIgnoreCase("PASS")) { if (!arguments.hasMoreTokens()) {
out.println("ưERR must supply password"); return;
}
String password = arguments.nextToken(); if (arguments.hasMoreTokens()) {
out.println("ưERR only one argument to PASS, your password"); return;
}
// Kiểm tra quyền đăng nhập
user = Server.storage.login(userName, password); if (user == null) {
out.println("ưERR invalid user or password"); state = ENTER_USER;
} else {
// user đó đăng nhập – trả về danh sỏch cỏc mail cú trong hộp thư messages = Server.storage.getMessages(user);
out.println("+OK mailbox open, " + countMessages() + " messages"); state = TRANSACTION;
} } else {
out.println("ưERR Only use PASS or QUIT commands"); } } /** * Xử lý lệnh USER */
protected void processEnterUser(String command, StringTokenizer arguments) {
if (command.equalsIgnoreCase("QUIT")) { stopRequested = true;
out.println("+OK Signing off"); return;
}
if (command.equalsIgnoreCase("USER")) { if (!arguments.hasMoreTokens()) {
out.println("ưERR must supply user name"); return;
}
if (arguments.hasMoreTokens()) {
out.println("ưERR only one argument to USER, the user name"); return;
}
state = ENTER_PASSWORD;
out.println("+OK use PASS command to send password"); return;
}
out.println("ưERR Only use USER or QUIT commands"); }
/**
* Xử lý lệnh LIST */
protected void processLIST(StringTokenizer arguments) { if (!arguments.hasMoreTokens()) {
out.println("+OK " + countMessages() + " " + getMessagesSize()); for (int i = 1; i <= messages.size(); i++) {
Message message = getMessage(i); if (message != null) {
} }
out.println("."); } else {
String messageNumber = arguments.nextToken(); Message message = getMessage(messageNumber); if (message == null) {
out.println("ưERR no such message"); } else {
out.println("+OK " + messageNumber + " " + message.getSize()); }
} }
/**
* Xử lý lệnh NOOP – Lệnh NOOP của POP3 khụng làm gỡ cả, mục đớch chỉ để trỡnh khỏch xem kết nối cũn hiệu lực hay khụng
*/
protected void processNOOP(StringTokenizer arguments) { out.println("+OK");
/**
* Xử lý lệnh QUIT */
protected void processQUIT(StringTokenizer arguments) { Enumeration enum = messages.elements();
while (enum.hasMoreElements()) {
Message message = (Message)enum.nextElement(); if (message.isDeleted()) {
Server.storage.deleteMessage(message); }
}
out.println("+OK Goodbye, " + user.getName()); stopRequested = true;
} /**
* Xử lý lệnh RETR trả về nội dung mail */
protected void processRETR(StringTokenizer arguments) { if (!arguments.hasMoreTokens()) {
} else {
String messageNumber = arguments.nextToken(); Message message = getMessage(messageNumber); if (message == null) {
out.println("ưERR no such message"); return;
}
out.println("+OK " + message.getSize() + " octets"); // Đọc dữ liệu mail
StringBuffer buffer = Server.storage.getMessageData(message);
BufferedReader reader = new BufferedReader(new StringReader(buffer.toString()));
// Đọc từng dũng boolean done = false; try {
while (reader.ready() && (!done)) {
if (line == null) break; // Dấu chấm kết thỳc if (line.length() >= 1) { if (line.substring(0, 1).equals(".")) line = "." + line; } // Gửi dữ liệu về trỡnh khỏch out.println(line); } } catch (IOException e) { System.err.println("POPConnection.processRETR()"); e.printStackTrace(System.err); } try { reader.close(); } catch (IOException e) { System.err.println("POPConnection.processRETR() ư reader.close()"); e.printStackTrace(System.err); }
out.println("."); } } /** * Xử lý lệnh RSET */
protected void processRSET(StringTokenizer arguments) { Enumeration enum = messages.elements();
while (enum.hasMoreElements()) {
Message message = (Message)enum.nextElement(); if (message.isDeleted()) message.setDeleted(false); } out.println("+OK"); } /** * Xử lý lệnh STAT */
protected void processSTAT(StringTokenizer arguments) {
} /**
* Xử lý lệnh TOP – trả về trỡnh khỏch cỏc mail header */
protected void processTOP(StringTokenizer arguments) { if (!arguments.hasMoreTokens()) {
out.println("ưERR syntax: TOP <msg> <lines>"); return;
}
String messageNumber = arguments.nextToken();
if (!arguments.hasMoreTokens()) {
out.println("ưERR syntax: TOP <msg> <lines>"); return; } int lines = 0; try { lines = Integer.parseInt(arguments.nextToken()); } catch (NumberFormatException e) {
out.println("ưERR bad number of lines"); return;
}
Message message = getMessage(messageNumber); if (message == null) {
out.println("ưERR no such message"); return;
}
out.println("+OK " + message.getSize() + " octets");
StringBuffer buffer = Server.storage.getMessageData(message);
BufferedReader reader = new BufferedReader(new StringReader(buffer.toString()));
boolean done = false; boolean inBody = false; int count = 0;
try {
while (reader.ready() && (!done) && (count <= lines)) { String line = reader.readLine();
if (line == null) break; if (line.length() >= 1) { if (line.substring(0, 1).equals(".")) line = "." + line; } else { inBody = true; } if (inBody) { count++; } out.println(line); } } catch (IOException e) { System.err.println("POPConnection.processTOP()"); e.printStackTrace(System.err); }
try { reader.close(); } catch (IOException e) { System.err.println("POPConnection.processTOP() ư reader.close()"); e.printStackTrace(System.err); } out.println("."); } /**
* Xử lý cỏc lệnh của giao thức POP3 */
protected void processTransaction(String command, StringTokenizer arguments) { if (command.equalsIgnoreCase("STAT")) { processSTAT(arguments); } else if (command.equalsIgnoreCase("LIST")) { processLIST(arguments); } else if (command.equalsIgnoreCase("RETR")) { processRETR(arguments); } else if (command.equalsIgnoreCase("DELE")) {
processDELE(arguments); } else if (command.equalsIgnoreCase("NOOP")) { processTOP(arguments); } else if (command.equalsIgnoreCase("TOP")) { processTOP(arguments); } else if (command.equalsIgnoreCase("UIDL")) { processUIDL(arguments); } else if (command.equalsIgnoreCase("RSET")) { processRSET(arguments); } else if (command.equalsIgnoreCase("QUIT")) { processQUIT(arguments); } else {
out.println("ưERR Unknown command " + command); }
}
protected void processUIDL(StringTokenizer arguments) { if (!arguments.hasMoreTokens()) {
out.println("+OK " + countMessages() + " " + getMessagesSize()); for (int i = 1; i <= messages.size(); i++) {
if (message != null) { out.println(i + " " + message.getMessageId()); } } out.println("."); } else {
String messageNumber = arguments.nextToken(); Message message = getMessage(messageNumber); if (message == null) {
out.println("ưERR no such message"); } else {
out.println("+OK " + messageNumber + " " + message.getMessageId()); }
} }
/**
* Nhận kết nối và cỏc lệnh POP3 gửi lờn từ trỡnh khỏch */
public void run() { try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintStream(socket.getOutputStream()); } catch (IOException e) { e.printStackTrace(System.err); close(); return; } if (server == null) {
System.err.println("SERVER NOT SET!!!"); return;
}
stopRequested = false;
state = ENTER_USER;
out.println("+OK POP3 " + Server.getAddress()); while (!stopRequested) {
String line = in.readLine(); if (line == null)
break;
StringTokenizer tokenizer = new StringTokenizer(line); String command = "";
if (tokenizer.hasMoreTokens())
command = tokenizer.nextToken(); // Xử lý lệnh tuỳ theo trạng thỏi đăng nhập của user
switch (state) { case ENTER_USER: processEnterUser(command, tokenizer); break; case ENTER_PASSWORD: processEnterPassword(command, tokenizer); break; case TRANSACTION: processTransaction(command, tokenizer); break; default:
System.err.println("Invalid State in POPConnection.run()"); stopRequested = true; break; } } catch (Exception e) { e.printStackTrace(System.err); stopRequested = true; } } // Đúng kết nối close(); server.removeConnection(this); }
public void setServer(POPServer server) {} }