主題: 利用純 J2SE 建立簡易的 Java 信件列表
目的: 實作一個簡易的信件列表並支持基礎的功能
描述: 此信件列表全由 J2SE 的套件撰寫, 沒有利用到 JavaMail 套件,
主要目的在於提供大家一個有關於 POP3 以及 SMTP 協定的
範例. 目前提供了幾項基礎功能: 允許使用者利用郵件對列表
進行註冊, 允許使用者利用郵件對列表進行註銷, 允許使用者發
信至列表並經由列表轉寄到每個已註冊之使用者的電郵信箱之
中.
使用方法:
主程式需要傳入三個參數:
arg0: 提供 SMTP 以及 POP3 的主機名稱
arg1: 使用者登入名稱或代號
arg2: 使用者登入密碼或口令
參考資料:
http://www.cjsdn.com/post/view?bid=7&id=52015&sty=3&keywords=POP3
程式碼:
import java.io.*;
import java.net.*;
import java.util.ArrayList;
/*
* MailingListServer.java
*
* MailingListServer is the legal property of its developer, whose name is tzutolin.
* Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* Created on 2004年4月16日, 下午 6:06
*/
/**
* J2SE 網路程式設計範例: 一個簡易的 Mailing List
* @author tzutolin
*/
public class MailingListServer {
private String host=null; //主機名稱
private String user=null; //使用者登入名稱
private String password=null; //口令或密碼
private ArrayList mailingList=null; //用以存放已註冊使用者的 ArrayList
private File mailingListFile=null; //用以存放已註冊使用者的檔案
private File archive=null; //封存檔案
/**
* MailingListServer 的建構子
* @param host 提供 POP3 以及 SMTP 的主機名稱
* @param user 使用者名稱或代號
* @param password 使用者密碼或口令
*/
public MailingListServer(String host, String user, String password){
this.host=host;
this.user=user;
this.password=password;
this.mailingList=new ArrayList();
this.mailingListFile=new File("C:/ml.list");
}
/* 1.POP3協定和伺服器
* POP3(Post Office Protocol version 3)是一種常用的網路協定,
* 用於從遠端伺服器的信箱裡收取E-mail,
* 它的常用命令根據連接時的不同狀態有︰
*
* (1)授權狀態(AUTHORIZATION state)
* User<SP><name><CRLF> 用戶名稱
* Pass<SP><string><CRLF> 用戶密碼
* Quit<CRLF> 退出
*
* (2)執行狀態(TRANSACTION state)
* STAT<CRLF>
* 信箱狀態,即信箱內共有幾封信,
* 總共大小(用以 8 進制表示)等。
* LIST<SP><msg><CRLF>
* 不用msg參數時顯示每封信的大小列表,
* 用msg參數時顯示編號為msg的信件的長度(8進製表示)。
* TOP<SP><msg><SP><n><CRLF>
* 取編號為msg的信件的標頭(head)和部分信體(body),
* n=0時只取信頭,n≠0時取信頭和信體的前n行。該命令為可
* 選命令,有些POP3伺服器軟體不支援它。
* RETR<SP><msg><CRLF>
* 讀取編號為msg的信件。
* DELE<SP><msg><CRLF>
* 刪編號為msg的信件,其實只是作個標記,
* 真正刪除要到更新狀態。
*
* (3)更新狀態(UPDATE state)
* QUIT<CRLF> 退出並把做過DELE標記的郵件刪掉。
* 另外還有NOOP、LAST、RSET、RPOP等命令用得較少。
* 與SMTP伺服器的情況相似,除了ISP提供的POP3伺服器
* 外,一些存放免費個人網頁的伺服器也提供POP3服務
* 。
*/
/**
* 開始執行 MailingList 的服務
*/
public void start(){
importMailingList();
listSubscribers();
try{
String reply=null;
int numberOfMails=0;
/* 建立 Socket 以及 I/O 串流以溝通 POP3 伺服器 */
Socket socket= new Socket(host, 110);
DataOutputStream out= new DataOutputStream(socket.getOutputStream());
DataInputStream in= new DataInputStream(socket.getInputStream());
reply=getSingleLineReply(in);
System.out.println(reply);
/* USER 名稱 */
out.writeBytes("USER " + user + "\r\n");
reply=getSingleLineReply(in);
System.out.println(reply);
/* PASSWORD 認證 */
out.writeBytes("PASS " + password + "\r\n");
reply=getSingleLineReply(in);
System.out.println(reply);
/* 信箱狀態, 並取回目前郵件數量 numberOfMails */
out.writeBytes("STAT "+ "\r\n");
reply=getSingleLineReply(in);
numberOfMails=getNumberOfMails(reply);
System.out.println("目前郵件數量為: "+numberOfMails);
/* 取得信件標頭 */
for(int i=0;i<numberOfMails;i++){
out.writeBytes("TOP "+String.valueOf(i+1)+" 0"+"\r\n");
reply=getMultipleLinesReply(in);
out.writeBytes("RETR "+String.valueOf(i+1)+"\r\n");
String temp=getMultipleLinesReply(in);
reply+=temp;
handleSubscriptionAndPost(reply);
}
/* 刪除所有信件 */
for(int i=0;i<numberOfMails;i++){
out.writeBytes("DELE "+String.valueOf(i+1)+"\r\n");
reply=getSingleLineReply(in);
System.out.println(reply);
}
/* 離開 POP3 伺服器並刷新資訊 */
out.writeBytes("QUIT\r\n");
reply=getSingleLineReply(in);
System.out.println(reply);
/* 關閉與 POP3 伺服器的連結 */
socket.close();
}catch(java.io.IOException ioe){
System.err.println("I/O 發生異常 @ doPOP3");
}
}
private String getSingleLineReply(DataInputStream in) {
String reply=null;
try {
reply= in.readLine();
}
catch (java.io.IOException IOException) {
System.err.println("I/O 發生異常 @ GetSingleLineReply");
}
return reply;
}
private String getMultipleLinesReply(DataInputStream in) {
String reply=null;
StringBuffer buffer=new StringBuffer();
try {
for (reply=in.readLine();(reply.equals(".")==false);reply=in.readLine()) {
buffer.append(reply+"\n");
}
}
catch (java.io.IOException IOException0) {
System.err.println("I/O 發生異常 @ GetMultipleLinesReply");
}
if(buffer.toString()!=null){
reply=buffer.toString();
}
return reply;
}
private int getNumberOfMails(String reply){
int numberOfMails=0;
if(reply.startsWith("+OK")==true){
String temp=reply.substring(reply.indexOf("K")+2,reply.lastIndexOf(" "));
numberOfMails=Integer.valueOf(temp).intValue();
}
return numberOfMails;
}
private void handleSubscriptionAndPost(String reply){
String senderMailAddress=null;
String subject=null;
if(reply.startsWith("+OK")==true){
String temp=reply.substring(reply.indexOf("From:"),reply.indexOf("To:"));
temp=temp.substring(temp.indexOf("<")+1,temp.indexOf(">"));
senderMailAddress=temp;
temp=reply.substring(reply.indexOf("Subject:")+9,reply.indexOf("Date:")-1);
subject=temp;
boolean isSubscriber=isSubscriber(senderMailAddress);
if(temp.indexOf("Subscribe")!=-1){
if(isSubscriber==false){
subscribe(senderMailAddress);
System.out.println("已註冊使用者: "+senderMailAddress);
exportMailingList();
}else{
System.out.println("使用者已經存在");
}
}else if(temp.indexOf("Unsubscribe")!=-1){
if(isSubscriber==true){
unsubscribe(senderMailAddress);
exportMailingList();
System.out.println("已移除使用者: "+senderMailAddress);
}else{
System.out.println("使用者不存在");
}
}else if(temp.indexOf("Post")!=-1){
System.out.println("寄發郵件");
saveToArchive(subject+senderMailAddress+".txt",reply);
post(subject,reply);
}
}
}
private boolean isSubscriber(String senderMailAddress){
return mailingList.contains(senderMailAddress);
}
private void subscribe(String senderMailAddress){
mailingList.add(senderMailAddress);
}
private void unsubscribe(String senderMailAddress){
mailingList.remove(senderMailAddress);
}
/*
* 1.SMTP協定和伺服器
* SMTP(Simple Mail Transfer Protocol)協議是目前網上流行的
* 發送E-mail的協議,SMTP協議共有14條命令。
* 不過,發一封E-mail只需用如下5條命令就足夠了(見下表)。
* <命令表格>
* HELO <SP> <domain> <CRLF>
* 與SMTP伺服器連結,並傳送本機域名
* MAIL <SP> FROM:<reverse-path> <CRLF>
* 傳送發信者的信箱名稱
* RCPT <SP> TO:<forward-path> <CRLF>
* 傳送接收者的信箱名稱
* DATA <CRLF> 發送信件數據(包括信頭和信體)
* QUIT <CRLF> 退出與SMTP伺服器的連接
* 除了ISP提供的SMTP伺服器以外,一些存放免費個人網頁的伺服器
* 的SMTP端口也是打開的,
* 如果該伺服器對外來的E-mail沒有增加RELAY限制,
* 那麼也可以把它當作SMTP伺服器來用。
*/
private void post(String subject, String content){
try{
/*開始按SMTP協議發信*/
for(int i=0;i<mailingList.size();i++){
Socket socket= new Socket(this.host, 25);
/*建立與smtp伺服器的連接*/
DataOutputStream out= new DataOutputStream(socket.getOutputStream());
DataInputStream in= new DataInputStream(socket.getInputStream());
BufferedReader buf=new BufferedReader(new InputStreamReader(in));
PrintStream os=new PrintStream(out);
String receiver=String.valueOf(mailingList.get);
String msg=null;
int state = 0; // 溝通狀態
// 將本文附上標頭
String body = "From: " + this.user+"@"+this.host + "\n" +
"To: " + receiver + "\n" +
"Subject: " + subject + "\n\n" +
content + "\n";
while((msg = buf.readLine()) != null) {
if(state == 0 && msg.charAt(0) == '2') {
os.print("HELO there\r\n");
state++;
}
else if(state == 1 && msg.charAt(0) == '2') {
os.print("MAIL FROM: <" + this.user+"@"+this.host + ">\r\n");
state++;
}
else if(state == 2 && msg.charAt(0) == '2') {
os.print("RCPT TO: <" + receiver + ">\r\n");
state++;
}
else if(state == 3 && msg.charAt(0) == '2') {
os.print("DATA\r\n");
state++;
}
else if(state == 4 && msg.charAt(0) == '3') {
os.print(body + "\r\n.\r\n");
state++;
}
else if(state == 5 && msg.charAt(0) == '2') {
os.print("QUIT\r\n");
state++;
}
else if(state == 6 && msg.charAt(0) == '2') {
System.out.println("郵件已傳送至 "+receiver);
break;
}
}
socket.close();
}
}catch(IOException ioe){
System.err.println("I/O 發生異常 @ post");
}
}
private void importMailingList(){
try{
FileInputStream fin=new FileInputStream(mailingListFile);
ObjectInputStream iout=new ObjectInputStream(fin);
mailingList=(ArrayList)iout.readObject();
iout.close();
fin.close();
}catch(Exception e){
System.err.println("I/O 發生異常 @ importMailingList");
}
}
private void exportMailingList(){
try{
FileOutputStream fout=new FileOutputStream(mailingListFile);
ObjectOutputStream oout=new ObjectOutputStream(fout);
oout.writeObject(mailingList);
oout.close();
fout.close();
}catch(Exception e){
System.err.println("I/O 發生異常 @ exportMailingList");
}
}
private void saveToArchive(String filename, String content){
archive=new File("C:/"+filename);
try{
FileOutputStream fout=new FileOutputStream(archive);
ObjectOutputStream oout=new ObjectOutputStream(fout);
oout.writeObject(content);
oout.close();
fout.close();
}catch(Exception e){
System.err.println("I/O 發生異常 @ saveToArchive");
}
}
private void listSubscribers(){
for(int i=0;i<mailingList.size();i++){
System.out.println(mailingList.get);
}
}
/**
* MailingListServer 的主方法
* @param args the command line arguments
* arg0 - 提供 POP3 以及 SMTP 的主機名稱
* arg1 - 使用者名稱或代號
* arg2 - 使用者密碼或口令
*/
public static void main(String[] args) {
MailingListServer mls=new MailingListServer(args[0], args[1], args[2]);
mls.start();
}
}
Best regards,
tzutolin