2.2.2 .Cỏc đơn thể của mailserver
2.2.2.1. Xõy dựng SMTPServer
Nhiệm vụchớnh của SMTP Server là sử lý tập lệnh SMTP, trả lại mó lỗi do trỡnh khỏch gởi lờn khụng hợp lệ. Tiếp nhận dữ liệu và lưu vào thư mục nhất định để trỡnh chủ POP3 Server cú thể truy xuất sau này. SMTP server sẽ mở socket lắng nghe trờn cổng 25 (cổng mặc định của SMTP). Ta cũng cú thể thay đổi số hiệu cổng trong file cấu hỡnh email.properties. khi nhận được kết nối từ trỡnh khỏch, SMTP server sẽ mở một tuyến (thread) là lớp SMTPConection chịu trỏch nhiệm phõn tớch cỏc lệnh SMTP và nhận mail do trỡnh khỏch gởi lờn. Lớp SMTP được cài đặt như sau:
SMTPServer.java
import java.io.*; import java.net.*;
import java.util.*;
public class SMTPServer extends Thread {
protected ServerSocket listenSocket = null; protected Vector connections;
public Boolean stopRequested;
public SMTPServer() throws Exception { int port = 0;
String portString = null;
// Lấy số hiệu cổng S MTP từ file cấu hỡnh try {
portString = Server.properties.getProperty("smtp.port"); port = Integer.parseInt(portString);
} catch (NumberFormatException e) {
throw new Exception("Invalid 'smtp.port' ư " + portString); }
// Mở socket lỏng nghe kết nối từ trỡnh khỏch listenSocket = new ServerSocket(port); // Mảng lưu cỏc kết nối từ trỡnh khỏch connections = new Vector(10, 10);
}
public void removeConnection(SMTPConnection connection) { connections.removeElement(connection);
}
public void run() {
// Lặp vụ tận chờ nhận kết nối cho đến khi cú tớn hiệu dừng stopRequested = new Boolean(false);
while (true) { synchronized (stopRequested) { if (stopRequested.booleanValue()) break; } // chấp nhận kết nối try { Socket s = listenSocket.accept(); System.out.println("Accept"); //Phõn tớch cỏc lệnh SMTP của trỡnh khỏch – thực hiện việc tiếp nhận mail
SMTPConnection connection = new SMTPConnection(s, this); connections.addElement(connection);
connection.start(); } catch (IOException e) { e.printStackTrace(); break; } } // Đúng kết nối try { listenSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
Lớp SMTPConnection đúng vai trũ chớnh trong SMTPServer. SMTPConnection phõn tớch cỏc lệnh SMTP, nhận mail và lưu và thư mục tương ứng với địa chỉ mail của User. Chương trỡnh mailserver cũng cho phộp khả năng relayvà chuyển tiếp mail. Cỏc mail cú địa chỉ khụng thuộc domain do mailserver quản lý sẽ được lưu và thư mục queue (hay hàng đợi). Chương trỡnh SMTPRelayServer và SMTPRForrwardServer sẽ xử lý cỏc mail này. Lớp SMTPConnection được cài đặt như sau:
SMTPConnection.java
import java.io.*; import java.net.*; import java.util.*; /**
* SMTPConnection : Xử lý cỏc lệnh SMTP, lưu mail vào thư mục thớch hợp */
public class SMTPConnection extends Thread { protected SMTPServer server;
protected Socket socket; protected Vector recipients; public BufferedReader in; public PrintStream out; public String returnPath; protected Vector users;
protected boolean stopRequested;
public SMTPConnection(Socket socket, SMTPServer server) { this.socket = socket;
this.server = server; }
/**
* Đúng kết nối – hoàn tất quỏ trỡnh nhận mail từ trỡnh khỏch * */
public void close() { try {
socket.close(); } catch (Exception e) {
System.err.println("Exception trying to close SMTPConnection socket!"); e.printStackTrace(System.err); } } /** * Xử lý lệnh SMTP DATA */
public void processDATA() throws IOException { String line;
StringBuffer data = new StringBuffer(); line = in.readLine();
if (line.startsWith(".."))
line = line.substring(1);
// Đặt chuỗi nhận được vào StringBuffer System.out.println(line);
data.append(line + "\n"); line = in.readLine(); }
// Lưu thụng điệp
String messageId = Server.storage.saveMessage(users, data); // Thụng bỏo cho trỡnh khỏch thụng điệp đó được lưu
out.println("250 Message '" + messageId + "' accepted for delivery"); }
/**
* Xử lý lệnh SMTP EHELO – Lệnh này chỉ dành cho tập lệnh của SMTP mở rộng – nú tương ứng với lệnh bắt tay HELO
**/
public void processEHLOCommand(StringTokenizer arguments) { processHELOCommand(arguments);
} /**
* Xử lý lệnh SMTP HELO **/
public void processHELOCommand(StringTokenizer arguments) { if (!arguments.hasMoreTokens()) {
out.println("501 HELO requires domain address"); return;
}
out.println("250 " + Server.getAddress() + " Hello"); }
/**
* Xử lý lệnh SMTP MAIL **/
public boolean processMAIL() throws IOException { String line = "";
while (true) {
line = in.readLine(); System.out.println(line);
if (line.length() < 4) {
out.println("500 Command Unknown '" + line + "'"); continue;
}
StringTokenizer tokenizer = new StringTokenizer(line); String command = tokenizer.nextToken();
if (command.equalsIgnoreCase("HELO")) { processHELOCommand(tokenizer); continue; } if (command.equalsIgnoreCase("EHLO")) { processEHLOCommand(tokenizer); continue; } if (command.equalsIgnoreCase("VRFY")) { processVRFYCommand(tokenizer); continue; } if (command.equalsIgnoreCase("QUIT")) { processQUITCommand(tokenizer); return false; }
if(command.equalsIgnoreCase("RCPT")||command.equalsIgnoreCase("DATA" )) {
out.println("503 Bad sequence of commands ư specify MAIL first"); continue; } if (command.equalsIgnoreCase("MAIL")) { if (processMAILCommand(tokenizer)) { out.println("250 OK"); return true; } }
out.println("500 Command Unknown '" + line + "'"); }
}
public boolean processMAILCommand(StringTokenizer arguments) { if (!arguments.hasMoreTokens()) {
out.println("503 Syntax: MAIL FROM:<user>"); return false;
returnPath = arguments.nextToken();
System.out.println(returnPath+" "+returnPath.length()); if (returnPath.length() < 5) {
out.println("503 Syntax: MAIL FROM:<user>"); return false;
}
if (!returnPath.substring(0, 5).equalsIgnoreCase("FROM:")) { out.println("503 Syntax: MAIL FROM:<user>"); return false;
}
return true; }
protected void processQUITCommand(StringTokenizer arguments) { out.println("221 " + Server.getAddress() + " closing connection"); stopRequested = true;
}
public boolean processRCPT() throws IOException { users = new Vector(2, 2);
String line = ""; while (true) {
line = in.readLine(); System.out.println(line);
if (line.length() < 4) {
out.println("500 Command Unknown '" + line + "'"); continue;
}
StringTokenizer tokenizer = new StringTokenizer(line); String command = tokenizer.nextToken();
if (command.equalsIgnoreCase("EHLO")) { processEHLOCommand(tokenizer); continue; } if (command.equalsIgnoreCase("VRFY")) { processVRFYCommand(tokenizer); continue; } if (command.equalsIgnoreCase("RSET")) { out.println("250 Reset state");
return false; } if (command.equalsIgnoreCase("QUIT")) { processQUITCommand(tokenizer); return false; } if (command.equalsIgnoreCase("MAIL")) {
out.println("503 Sender already specified"); continue;
}
if (command.equalsIgnoreCase("DATA")) { if (users.size() == 0) {
out.println("503 Bad sequence of commands ư specify RCPT first"); continue;
}
out.println("354 Enter mail, ending with '.' on a line by itself"); return true;
}
if (command.equalsIgnoreCase("RCPT")) { processRCPTCommand(tokenizer);
continue; }
out.println("500 Command Unknown '" + line + "'"); }
}
public void processRCPTCommand(StringTokenizer arguments) { if (!arguments.hasMoreTokens()) {
out.println("501 Syntax: RCPT TO:<address>"); return;
}
String arg = arguments.nextToken();
if (!arg.substring(0, 3).equalsIgnoreCase("TO:")) {
out.println("501 Syntax: RCPT TO:<address>"); return;
}
// Địa chỉ nằm sau "TO:" System.out.println(arg);
String address; try{
address=arguments.nextToken(); //cú khoảng trắng } catch(Exception e){
address = arg.substring(3);//khụng cú khoảng trắng }
System.out.println("Receipt address :"+address);
// Thụng thường địa chỉ mail được gởi theo dạng <user@domainname>
if (address.substring(0, 1).equals("<") && address.substring(address.length() ư 1, address.length()).equals(">"))
address = address.substring(1, address.length() ư 1); // Lấy về thụng tin của user từ địa chỉ mail
User user = Server.storage.getUser(address); // Bỏo lỗi cho trỡnh khỏch nếu user khụng tồn tại
if (user == null) {
out.println("550 User " + address + " is not known"); return;
}
users.addElement(user); // Trả về mó lỗi thành cụng
out.println("250 Recipient " + address + " ok"); }
public void processVRFYCommand(StringTokenizer arguments) { out.println("252 VRFY command not implemented");
} /**
* Phương thức run() lặp liờn tục để ư lý lệnh cho đến khi hoàn tất */
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; } stopRequested = false;
out.println("220mail.goemaat.comJAVASMTPServer (com.goemaat.email.SMTP) ready"); while (!stopRequested) { try { if (processMAIL()) { if (processRCPT()) processDATA(); } } catch (IOException e) { e.printStackTrace(System.err); stopRequested = true; } } // Đúng socket đó mở trước đú close();
// Loại bỏ kết nối trong danh sỏch SMTPServer server.removeConnection(this);
} }
Chương trỡnh Server.java dưới đõy được dựng để đọc file cấu hỡnh email.properties, khởi tạo SMTPServer. Server.java cũng làm nhiệm vụ khởi tạo trỡnh chủ POPServer. Trỡnh Server.java được cài đặt như sau:
Server.java
import java.io.*; import java.util.*; public class Server {
public static Properties properties; public static SMTPServer smtp; public static EmailStorage storage; public static POPServer pop;
public static String getAddress() {
String address = properties.getProperty("server.address"); if (address == null)
address = "UNKNOWN[server.address]"; return address;
}
protected static boolean getProperties() { try {
String fileName = "email.properties";
// Kiểm tra sự tồn tại của file cấu hỡnh email.properties File file = new File(fileName);
if (!file.exists()) {
fileName = file.getAbsolutePath();
System.out.println("Specified properties file '" + file.getAbsolutePath() + "' does not exist!");
return false; }
// Đọc cỏc thụng tin cấu hỡnh properties = new Properties();
FileInputStream in = new FileInputStream(file); properties.load(in); in.close(); } catch (Exception e) { System.out.println(e); return false; } System.getProperties().put("line.separator", "\r\n");
return true; }
/**
* Chương trỡnh chớnh */
public static void main(String[] args) {
if (!getProperties()) {
System.err.println("Could not get properties!"); return;
}
System.out.println(Server.properties.getProperty("smtp.port")); // Thiết lập nơi lưu trữ mail
if (!setupStorage()) {
System.out.println("Could not setup storage!"); return;
}
// Khởi tạo SMTP server if (!startSMTPServer()) {
return; }
// Khởi tạo POP server if (!startPOPServer()) {
System.err.println("Could not start POP server!"); return;
} }
/**
*Phương thức khởi tạo nơi lưu trữ mail */
public static boolean setupStorage() {
String className = properties.getProperty("storage.class"); if (className == null || className.length() == 0) {
System.err.println("No storage class specified in property 'storage.class'!");
return false; }
try {
c = Class.forName(className); } catch (ClassNotFoundException e) {
System.err.println("Class '" + className + "' not found! (check CLASSPATH)");
e.printStackTrace(System.err); return false;
}
// Tạo thể hiện của lớp try { storage = (EmailStorage)c.newInstance(); } catch (InstantiationException e) { e.printStackTrace(System.err); return false; } catch (IllegalAccessException e) { e.printStackTrace(System.err); return false; } return storage.init(); }
/**
* Khởi tạo POP server. */
protected static boolean startPOPServer() { try {
pop = new POPServer(); pop.setDaemon(false); pop.start(); } catch (Exception e) { e.printStackTrace(System.err); return false; } return true; } /**
* Khởi tạo SMTP server. */
protected static boolean startSMTPServer() { try {
smtp = new SMTPServer(); smtp.setDaemon(false); smtp.start(); } catch (Exception e) { e.printStackTrace(System.err); return false; } return true; } }