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;
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;
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);
} /**
**/
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;
}
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());
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) {
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;
}
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);
// Một vài trình khách gởi lệnh RCPT TO: không có khoảng trắng sau TO
String address; try{
address=arguments.nextToken(); //có khoảng trắng
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;
}
// Đưa user vào danh sách phân phối mail
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 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()) {
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()) {
System.err.println("Could not start SMTP server!"); 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; } Class c = null; 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 {
} 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; } }