using system;
using system.text;
using system.io;
using system.net;
using system.net.sockets;
using system.collections;
namespace skydev.web.mail
{
public enum mailformat{text,html};
public enum mailpriority{low=1,normal=3,high=5};
#region class mailattachments
public class mailattachments
{
private const int maxattachmentnum=10;
private ilist _attachments;
public mailattachments()
{
_attachments=new arraylist();
}
public string this[int index]
{
get { return (string)_attachments[index];}
}
/// <summary>
/// 添加郵件附件
/// </summary>
/// <param name="filepath">附件的絕對路徑</param>
public void add(params string[] filepath)
{
if(filepath==null)
{
throw(new argumentnullexception("非法的附件"));
}
else
{
for(int i=0;i<filepath.length;i++)
{
add(filepath[i]);
}
}
}
/// <summary>
/// 添加一個附件,當(dāng)指定的附件不存在時,忽略該附件,不產(chǎn)生異常。
/// </summary>
/// <param name="filepath">附件的絕對路徑</param>
public void add(string filepath)
{
//當(dāng)附件存在時才加入,否則忽略
if (system.io.file.exists(filepath))
{
if (_attachments.count<maxattachmentnum)
{
_attachments.add(filepath);
}
}
}
public void clear()//清除所有附件
{
_attachments.clear();
}
public int count//獲取附件個數(shù)
{
get { return _attachments.count;}
}
}
#endregion//end class mailattachments
#region class mailmessage
/// <summary>
/// mailmessage 表示smtp要發(fā)送的一封郵件的消息。
/// </summary>
public class mailmessage
{
private const int maxrecipientnum=10;
public mailmessage()
{
_recipients=new arraylist();//收件人列表
_attachments=new mailattachments();//附件
_bodyformat=mailformat.text;//缺省的郵件格式為text
_priority=mailpriority.normal;
_charset="gb2312";
}
/// <summary>
/// 設(shè)定語言代碼,默認(rèn)設(shè)定為gb2312,如不需要可設(shè)置為""
/// </summary>
public string charset
{
get { return _charset;}
set { _charset=value;}
}
public string from
{
get{ return _from;}
set { _from=value;}
}
public string fromname
{
get { return _fromname;}
set { _fromname=value;}
}
public string body
{
get { return _body;}
set { _body=value;}
}
public string subject
{
get { return _subject;}
set { _subject=value;}
}
public mailattachments attachments
{
get {return _attachments;}
set { _attachments=value;}
}
public mailpriority priority
{
get { return _priority;}
set { _priority=value;}
}
public ilist recipients
{
get { return _recipients;}
}
/// <summary>
/// 增加一個收件人地址
/// </summary>
/// <param name="recipient">收件人的email地址</param>
public void addrecipients(string recipient)
{
//先檢查郵件地址是否符合規(guī)范
if (_recipients.count<maxrecipientnum)
{
_recipients.add(recipient);//增加到收件人列表
}
}
public void addrecipients(params string[] recipient)
{
if (recipient==null)
{
throw (new argumentexception("收件人不能為空."));
}
else
{
for (int i=0;i<recipient.length;i++)
{
addrecipients(recipient[i]);
}
}
}
public mailformat bodyformat
{
set { _bodyformat=value;}
get { return _bodyformat;}
}
private string _from;//發(fā)件人地址
private string _fromname;//發(fā)件人姓名
private ilist _recipients;//收件人
private mailattachments _attachments;//附件
private string _body;//內(nèi)容
private string _subject;//主題
private mailformat _bodyformat;//郵件格式
private string _charset="gb2312";//字符編碼格式
private mailpriority _priority;//郵件優(yōu)先級
}
#endregion
#region class smtpmail
public class smtpserverhelper
{
private string crlf="/r/n";//回車換行
/// <summary>
/// 錯誤消息反饋
/// </summary>
private string errmsg;
/// <summary>
/// tcpclient對象,用于連接服務(wù)器
/// </summary>
private tcpclient tcpclient;
/// <summary>
/// networkstream對象
/// </summary>
private networkstream networkstream;
/// <summary>
/// 服務(wù)器交互記錄
/// </summary>
private string logs="";
/// <summary>
/// smtp錯誤代碼哈希表
/// </summary>
private hashtable errcodeht = new hashtable();
/// <summary>
/// smtp正確代碼哈希表
/// </summary>
private hashtable rightcodeht = new hashtable();
public smtpserverhelper()
{
smtpcodeadd();//初始化smtpcode
}
~smtpserverhelper()
{
networkstream.close();
tcpclient.close();
}
/// <summary>
/// 將字符串編碼為base64字符串
/// </summary>
/// <param name="str">要編碼的字符串</param>
private string base64encode(string str)
{
byte[] barray;
barray=encoding.default.getbytes(str);
return convert.tobase64string(barray);
}
/// <summary>
/// 將base64字符串解碼為普通字符串
/// </summary>
/// <param name="str">要解碼的字符串</param>
private string base64decode(string str)
{
byte[] barray;
barray=convert.frombase64string(str);
return encoding.default.getstring(barray);
}
/// <summary>
/// 得到上傳附件的文件流
/// </summary>
/// <param name="filepath">附件的絕對路徑</param>
private string getstream(string filepath)
{
//建立文件流對象
system.io.filestream filestr=new system.io.filestream(filepath,system.io.filemode.open);
byte[] by=new byte[system.convert.toint32(filestr.length)];
filestr.read(by,0,by.length);
filestr.close();
return(system.convert.tobase64string(by));
}
/// <summary>
/// smtp回應(yīng)代碼哈希表
/// </summary>
private void smtpcodeadd()
{
//[rfc 821 4.2.1.]
/*
4.2.2. numeric order list of reply codes
211 system status, or system help reply
214 help message
[information on how to use the receiver or the meaning of a
particular non-standard command; this reply is useful only
to the human user]
220 <domain> service ready
221 <domain> service closing transmission channel
250 requested mail action okay, completed
251 user not local; will forward to <forward-path>
354 start mail input; end with <crlf>.<crlf>
421 <domain> service not available,
closing transmission channel
[this may be a reply to any command if the service knows it
must shut down]
450 requested mail action not taken: mailbox unavailable
[e.g., mailbox busy]
451 requested action aborted: local error in processing
452 requested action not taken: insufficient system storage
500 syntax error, command unrecognized
[this may include errors such as command line too long]
501 syntax error in parameters or arguments
502 command not implemented
503 bad sequence of commands
504 command parameter not implemented
550 requested action not taken: mailbox unavailable
[e.g., mailbox not found, no access]
551 user not local; please try <forward-path>
552 requested mail action aborted: exceeded storage allocation
553 requested action not taken: mailbox name not allowed
[e.g., mailbox syntax incorrect]
554 transaction failed
*/
errcodeht.add("421","服務(wù)未就緒,關(guān)閉傳輸信道");
errcodeht.add("432","需要一個密碼轉(zhuǎn)換");
errcodeht.add("450","要求的郵件操作未完成,郵箱不可用(例如,郵箱忙)");
errcodeht.add("451","放棄要求的操作;處理過程中出錯");
errcodeht.add("452","系統(tǒng)存儲不足,要求的操作未執(zhí)行");
errcodeht.add("454","臨時認(rèn)證失敗");
errcodeht.add("500","郵箱地址錯誤");
errcodeht.add("501","參數(shù)格式錯誤");
errcodeht.add("502","命令不可實現(xiàn)");
errcodeht.add("503","服務(wù)器需要smtp驗證");
errcodeht.add("504","命令參數(shù)不可實現(xiàn)");
errcodeht.add("530","需要認(rèn)證");
errcodeht.add("534","認(rèn)證機(jī)制過于簡單");
errcodeht.add("538","當(dāng)前請求的認(rèn)證機(jī)制需要加密");
errcodeht.add("550","要求的郵件操作未完成,郵箱不可用(例如,郵箱未找到,或不可訪問)");
errcodeht.add("551","用戶非本地,請嘗試<forward-path>");
errcodeht.add("552","過量的存儲分配,要求的操作未執(zhí)行");
errcodeht.add("553","郵箱名不可用,要求的操作未執(zhí)行(例如郵箱格式錯誤)");
errcodeht.add("554","傳輸失敗");
/*
211 system status, or system help reply
214 help message
[information on how to use the receiver or the meaning of a
particular non-standard command; this reply is useful only
to the human user]
220 <domain> service ready
221 <domain> service closing transmission channel
250 requested mail action okay, completed
251 user not local; will forward to <forward-path>
354 start mail input; end with <crlf>.<crlf>
*/
rightcodeht.add("220","服務(wù)就緒");
rightcodeht.add("221","服務(wù)關(guān)閉傳輸信道");
rightcodeht.add("235","驗證成功");
rightcodeht.add("250","要求的郵件操作完成");
rightcodeht.add("251","非本地用戶,將轉(zhuǎn)發(fā)向<forward-path>");
rightcodeht.add("334","服務(wù)器響應(yīng)驗證base64字符串");
rightcodeht.add("354","開始郵件輸入,以<crlf>.<crlf>結(jié)束");
}
/// <summary>
/// 發(fā)送smtp命令
/// </summary>
private bool sendcommand(string str)
{
byte[]writebuffer;
if(str==null||str.trim()==string.empty)
{
return true;
}
logs+=str;
writebuffer = encoding.default.getbytes(str);
try
{
networkstream.write(writebuffer,0,writebuffer.length);
}
catch
{
errmsg="網(wǎng)絡(luò)連接錯誤";
return false;
}
return true;
}
/// <summary>
/// 接收smtp服務(wù)器回應(yīng)
/// </summary>
private string recvresponse()
{
int streamsize;
string returnvalue = string.empty;
byte[] readbuffer = new byte[1024] ;
try
{
streamsize=networkstream.read(readbuffer,0,readbuffer.length);
}
catch
{
errmsg="網(wǎng)絡(luò)連接錯誤";
return "false";
}
if (streamsize==0)
{
return returnvalue ;
}
else
{
returnvalue = encoding.default.getstring(readbuffer).substring(0,streamsize);
logs+=returnvalue+this.crlf;
return returnvalue;
}
}
/// <summary>
/// 與服務(wù)器交互,發(fā)送一條命令并接收回應(yīng)。
/// </summary>
/// <param name="str">一個要發(fā)送的命令</param>
/// <param name="errstr">如果錯誤,要反饋的信息</param>
private bool dialog(string str,string errstr)
{
if(str==null||str.trim()==string.empty)
{
return true;
}
if(sendcommand(str))
{
string rr=recvresponse();
if(rr=="false")
{
return false;
}
//檢查返回的代碼,根據(jù)[rfc 821]返回代碼為3位數(shù)字代碼如220
string rrcode=rr.substring(0,3);
if(rightcodeht[rrcode]!=null)
{
return true;
}
else
{
if(errcodeht[rrcode]!=null)
{
errmsg+=(rrcode+errcodeht[rrcode].tostring());
errmsg+=crlf;
}
else
{
errmsg+=rr;
}
errmsg+=errstr;
return false;
}
}
else
{
return false;
}
}
/// <summary>
/// 與服務(wù)器交互,發(fā)送一組命令并接收回應(yīng)。
/// </summary>
private bool dialog(string[] str,string errstr)
{
for(int i=0;i<str.length;i++)
{
if(!dialog(str[i],""))
{
errmsg+=crlf;
errmsg+=errstr;
return false;
}
}
return true;
}
//連接服務(wù)器
private bool connect(string smtpserver,int port)
{
//創(chuàng)建tcp連接
try
{
tcpclient=new tcpclient(smtpserver,port);
}
catch(exception e)
{
errmsg=e.tostring();
return false;
}
networkstream=tcpclient.getstream();
//驗證網(wǎng)絡(luò)連接是否正確
if(rightcodeht[recvresponse().substring(0,3)]==null)
{
errmsg="網(wǎng)絡(luò)連接失敗";
return false;
}
return true;
}
private string getprioritystring(mailpriority mailpriority)
{
string priority="normal";
if (mailpriority==mailpriority.low)
{
priority="low";
}
else if (mailpriority==mailpriority.high)
{
priority="high";
}
return priority;
}
/// <summary>
/// 發(fā)送電子郵件,smtp服務(wù)器不需要身份驗證
/// </summary>
/// <param name="smtpserver"></param>
/// <param name="port"></param>
/// <param name="mailmessage"></param>
/// <returns></returns>
public bool sendemail(string smtpserver,int port,mailmessage mailmessage)
{
return sendemail(smtpserver,port,false,"","",mailmessage);
}
/// <summary>
/// 發(fā)送電子郵件,smtp服務(wù)器需要身份驗證
/// </summary>
/// <param name="smtpserver"></param>
/// <param name="port"></param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="mailmessage"></param>
/// <returns></returns>
public bool sendemail(string smtpserver,int port,string username,string password,mailmessage mailmessage)
{
return sendemail(smtpserver,port,false,username,password,mailmessage);
}
private bool sendemail(string smtpserver,int port,bool esmtp,string username,string password,mailmessage mailmessage)
{
if (connect(smtpserver,port)==false)//測試連接服務(wù)器是否成功
return false;
string priority=getprioritystring(mailmessage.priority);
bool html=(mailmessage.bodyformat==mailformat.html);
string[] sendbuffer;
string sendbufferstr;
//進(jìn)行smtp驗證,現(xiàn)在大部分smtp服務(wù)器都要認(rèn)證
if(esmtp)
{
sendbuffer=new string[4];
sendbuffer[0]="ehlo " + smtpserver + crlf;
sendbuffer[1]="auth login" + crlf;
sendbuffer[2]=base64encode(username) + crlf;
sendbuffer[3]=base64encode(password) + crlf;
if(!dialog(sendbuffer,"smtp服務(wù)器驗證失敗,請核對用戶名和密碼。"))
return false;
}
else
{//不需要身份認(rèn)證
sendbufferstr="helo " + smtpserver + crlf;
if(!dialog(sendbufferstr,""))
return false;
}
//發(fā)件人地址
sendbufferstr="mail from:<" + mailmessage.from + ">" + crlf;
if(!dialog(sendbufferstr,"發(fā)件人地址錯誤,或不能為空"))
return false;
//收件人地址
sendbuffer=new string[mailmessage.recipients.count];
for(int i=0;i<mailmessage.recipients.count;i++)
{
sendbuffer[i]="rcpt to:<" +(string)mailmessage.recipients[i] +">" + crlf;
}
if(!dialog(sendbuffer,"收件人地址有誤"))
return false;
/*
sendbuffer=new string[10];
for(int i=0;i<recipientbcc.count;i++)
{
sendbuffer[i]="rcpt to:<" + recipientbcc[i].tostring() +">" + crlf;
}
if(!dialog(sendbuffer,"密件收件人地址有誤"))
return false;
*/
sendbufferstr="data" + crlf;
if(!dialog(sendbufferstr,""))
return false;
//發(fā)件人姓名
sendbufferstr="from:" + mailmessage.fromname + "<" +mailmessage.from +">" +crlf;
//if(replyto.trim()!="")
//{
// sendbufferstr+="reply-to: " + replyto + crlf;
//}
//sendbufferstr+="to:" + toname + "<" + recipient[0] +">" +crlf;
//至少要有一個收件人
if (mailmessage.recipients.count==0)
{
return false;
}
else
{
sendbufferstr += "to:=?"+mailmessage.charset.toupper()+"?b?"+
base64encode((string)mailmessage.recipients[0])+"?="+"<"+(string)mailmessage.recipients[0]+">"+crlf;
}
//sendbufferstr+="cc:";
//for(int i=0;i<recipient.count;i++)
//{
// sendbufferstr+=recipient[i].tostring() + "<" + recipient[i].tostring() +">,";
//}
//sendbufferstr+=crlf;
sendbufferstr+=
((mailmessage.subject==string.empty || mailmessage.subject==null)?"subject:":((mailmessage.charset=="")?("subject:" +
mailmessage.subject):("subject:" + "=?" + mailmessage.charset.toupper() + "?b?" +
base64encode(mailmessage.subject) +"?="))) + crlf;
sendbufferstr+="x-priority:" + priority + crlf;
sendbufferstr+="x-msmail-priority:" + priority + crlf;
sendbufferstr+="importance:" + priority + crlf;
sendbufferstr+="x-mailer: lion.web.mail.smtpmail pubclass [cn]" + crlf;
sendbufferstr+="mime-version: 1.0" + crlf;
if(mailmessage.attachments.count!=0)
{
sendbufferstr+="content-type: multipart/mixed;" + crlf;
sendbufferstr += " boundary=/"====="+
(html?"001_dragon520636771063_":"001_dragon303406132050_")+"=====/""+crlf+crlf;
}
if(html)
{
if(mailmessage.attachments.count==0)
{
sendbufferstr += "content-type: multipart/alternative;"+crlf;//內(nèi)容格式和分隔符
sendbufferstr += " boundary=/"=====003_dragon520636771063_=====/""+crlf+crlf;
sendbufferstr += "this is a multi-part message in mime format."+crlf+crlf;
}
else
{
sendbufferstr +="this is a multi-part message in mime format."+crlf+crlf;
sendbufferstr += "--=====001_dragon520636771063_====="+crlf;
sendbufferstr += "content-type: multipart/alternative;"+crlf;//內(nèi)容格式和分隔符
sendbufferstr += " boundary=/"=====003_dragon520636771063_=====/""+crlf+crlf;
}
sendbufferstr += "--=====003_dragon520636771063_====="+crlf;
sendbufferstr += "content-type: text/plain;"+ crlf;
sendbufferstr += ((mailmessage.charset=="")?(" charset=/"iso-8859-1/""):(" charset=/"" +
mailmessage.charset.tolower() + "/"")) + crlf;
sendbufferstr+="content-transfer-encoding: base64" + crlf + crlf;
sendbufferstr+= base64encode("郵件內(nèi)容為html格式,請選擇html方式查看") + crlf + crlf;
sendbufferstr += "--=====003_dragon520636771063_====="+crlf;
sendbufferstr+="content-type: text/html;" + crlf;
sendbufferstr+=((mailmessage.charset=="")?(" charset=/"iso-8859-1/""):(" charset=/"" +
mailmessage.charset.tolower() + "/"")) + crlf;
sendbufferstr+="content-transfer-encoding: base64" + crlf + crlf;
sendbufferstr+= base64encode(mailmessage.body) + crlf + crlf;
sendbufferstr += "--=====003_dragon520636771063_=====--"+crlf;
}
else
{
if(mailmessage.attachments.count!=0)
{
sendbufferstr += "--=====001_dragon303406132050_====="+crlf;
}
sendbufferstr+="content-type: text/plain;" + crlf;
sendbufferstr+=((mailmessage.charset=="")?(" charset=/"iso-8859-1/""):(" charset=/"" +
mailmessage.charset.tolower() + "/"")) + crlf;
sendbufferstr+="content-transfer-encoding: base64" + crlf + crlf;
sendbufferstr+= base64encode(mailmessage.body) + crlf;
}
//sendbufferstr += "content-transfer-encoding: base64"+crlf;
if(mailmessage.attachments.count!=0)
{
for(int i=0;i<mailmessage.attachments.count;i++)
{
string filepath = (string)mailmessage.attachments[i];
sendbufferstr += "--====="+
(html?"001_dragon520636771063_":"001_dragon303406132050_") +"====="+crlf;
//sendbufferstr += "content-type: application/octet-stream"+crlf;
sendbufferstr += "content-type: text/plain;"+crlf;
sendbufferstr += " name=/"=?"+mailmessage.charset.toupper()+"?b?"+
base64encode(filepath.substring(filepath.lastindexof("//")+1))+"?=/""+crlf;
sendbufferstr += "content-transfer-encoding: base64"+crlf;
sendbufferstr += "content-disposition: attachment;"+crlf;
sendbufferstr += " filename=/"=?"+mailmessage.charset.toupper()+"?b?"+
base64encode(filepath.substring(filepath.lastindexof("//")+1))+"?=/""+crlf+crlf;
sendbufferstr += getstream(filepath)+crlf+crlf;
}
sendbufferstr += "--====="+
(html?"001_dragon520636771063_":"001_dragon303406132050_")+"=====--"+crlf+crlf;
}
sendbufferstr += crlf + "." + crlf;//內(nèi)容結(jié)束
if(!dialog(sendbufferstr,"錯誤信件信息"))
return false;
sendbufferstr="quit" + crlf;
if(!dialog(sendbufferstr,"斷開連接時錯誤"))
return false;
networkstream.close();
tcpclient.close();
return true;
}
}
public class smtpmail
{
private static string _smtpserver;
/// <summary>
/// 格式:smtpaccount:[email protected]<br>
/// 或者:smtpserveraddress<br>
/// <code>
/// smtpmail.smtpserver="user:[email protected]";
/// //或者:
/// smtpmail.smtpserver="smtp.126.com";
/// 或者:
/// smtpmail.smtpserver=smtpserverhelper.getsmtpserver("user","12345678","smtp.126.com");
/// </code>
/// </summary>
public static string smtpserver
{
set { _smtpserver=value;}
get { return _smtpserver;}
}
public static bool send(mailmessage mailmessage,string username,string password)
{
smtpserverhelper helper=new smtpserverhelper();
return helper.sendemail(_smtpserver,25,username,password,mailmessage);
}
}
#endregion
}
using system;
using nunit.framework;
namespace skydev.web.mail
{
/// <summary>
/// test 的摘要說明。
/// </summary>
[testfixture]
public class testsmtpmail
{
//安裝測試用例,完成初始化操作
[setup]
public void setup()
{
}
//測試結(jié)束完成清理操作
[teardown]
public void teardown()
{
}
[test]
public void testmailattachments()
{
skydev.web.mail.mailattachments attachments=new mailattachments();
assert.areequal(0,attachments.count,"初始化mailattachments");
attachments.add("c://autoexec.bat");
assert.areequal(1,attachments.count,"增加附件(附件確實存在)");
attachments.add("c://autoexec.dat.txt");
assert.areequal(1,attachments.count,"增加附件(附件不存在)");
attachments.clear();
assert.areequal(0,attachments.count,"清除附件");
}
[test]
public void testmailmessage()
{
mailmessage message=new mailmessage();
assert.areequal(0,message.attachments.count,"初始化mailattachments");
assert.areequal(mailformat.text,message.bodyformat,"郵件格式");
assert.areequal("gb2312",message.charset,"缺省的字符集");
}
[test]
public void testsendmail()
{
smtpmail.smtpserver="smtp.126.com";
mailmessage mail=new mailmessage();
mail.from="[email protected]";
mail.fromname="曾青松";
mail.addrecipients("[email protected]");
mail.subject="主題:測試郵件";
mail.bodyformat=mailformat.text;
mail.body="測試的內(nèi)容.";
mail.attachments.add("c://test.txt");
smtpmail.send(mail,"","");//請?zhí)顚懽约旱臏y試郵件帳號
}
}
}