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];}
}
///
1<summary>
2/// 添加邮件附件
3/// </summary>
///
1<param name="FilePath"/>
附件的绝对路径
public void Add(params string[] filePath)
{
if(filePath==null)
{
throw(new ArgumentNullException("非法的附件"));
}
else
{
for(int i=0;i
1<filepath.length;i++) <summary="" add(filepath[i]);="" {="" }="">
2/// 添加一个附件,当指定的附件不存在时,忽略该附件,不产生异常。
3///
4/// <param name="filePath"/>附件的绝对路径
5public void Add(string filePath)
6{
7//当附件存在时才加入,否则忽略
8if (System.IO.File.Exists(filePath))
9{
10if (_Attachments.Count<maxattachmentnum) #endregion="" #region="" <summary="" _attachments.add(filepath);="" _attachments.clear();="" _attachments.count;}="" class="" clear()="" count="" end="" get="" int="" mailattachments="" mailmessage="" public="" return="" void="" {="" }="" 清除所有附件="" 获取附件个数="">
11/// MailMessage 表示SMTP要发送的一封邮件的消息。
12///
13public class MailMessage
14{
15private const int MaxRecipientNum=10;
16public MailMessage()
17{
18_Recipients=new ArrayList();//收件人列表
19_Attachments=new MailAttachments();//附件
20_BodyFormat=MailFormat.Text;//缺省的邮件格式为Text
21_Priority=MailPriority.Normal;
22_Charset="GB2312";
23}
24
25/// <summary>
26/// 设定语言代码,默认设定为GB2312,如不需要可设置为""
27/// </summary>
28public string Charset
29{
30get { return _Charset;}
31set { _Charset=value;}
32}
33
34public string From
35{
36get{ return _From;}
37set { _From=value;}
38}
39
40public string FromName
41{
42get { return _FromName;}
43set { _FromName=value;}
44}
45public string Body
46{
47get { return _Body;}
48set { _Body=value;}
49}
50
51public string Subject
52{
53get { return _Subject;}
54set { _Subject=value;}
55}
56
57public MailAttachments Attachments
58{
59get {return _Attachments;}
60set { _Attachments=value;}
61}
62
63public MailPriority Priority
64{
65get { return _Priority;}
66set { _Priority=value;}
67}
68
69public IList Recipients
70{
71get { return _Recipients;}
72}
73/// <summary>
74/// 增加一个收件人地址
75/// </summary>
76/// <param name="recipient"/>收件人的Email地址
77public void AddRecipients(string recipient)
78{
79//先检查邮件地址是否符合规范
80if (_Recipients.Count<maxrecipientnum) #endregion="" #region="" (int="" (new="" (recipient="null)" ;="" <summary="" _attachments;="" _body;="" _bodyformat="value;}" _bodyformat;="" _bodyformat;}="" _charset="GB2312" _from;="" _fromname;="" _priority;="" _recipients.add(recipient);="" _recipients;="" _subject;="" addrecipients(params="" addrecipients(recipient[i]);="" argumentexception("收件人不能为空."));="" bodyformat="" class="" crlf="\r\n" else="" for="" get="" i="0;i<recipient.Length;i++)" if="" ilist="" mailattachments="" mailformat="" mailpriority="" private="" public="" recipient)="" return="" set="" smtpmail="" smtpserverhelper="" string="" string[]="" throw="" void="" {="" }="" 主题="" 内容="" 发件人地址="" 发件人姓名="" 回车换行="" 增加到收件人列表="" 字符编码格式="" 收件人="" 邮件优先级="" 邮件格式="" 附件="">
81/// 错误消息反馈
82///
83private string errmsg;
84
85/// <summary>
86/// TcpClient对象,用于连接服务器
87/// </summary>
88private TcpClient tcpClient;
89
90/// <summary>
91/// NetworkStream对象
92/// </summary>
93private NetworkStream networkStream;
94
95/// <summary>
96/// 服务器交互记录
97/// </summary>
98private string logs="";
99
100/// <summary>
101/// SMTP错误代码哈希表
102/// </summary>
103private Hashtable ErrCodeHT = new Hashtable();
104
105/// <summary>
106/// SMTP正确代码哈希表
107/// </summary>
108private Hashtable RightCodeHT = new Hashtable();
109
110public SmtpServerHelper()
111{
112SMTPCodeAdd();//初始化SMTPCode
113}
114
115~SmtpServerHelper()
116{
117networkStream.Close();
118tcpClient.Close();
119}
120
121/// <summary>
122/// 将字符串编码为Base64字符串
123/// </summary>
124/// <param name="str"/>要编码的字符串
125private string Base64Encode(string str)
126{
127byte[] barray;
128barray=Encoding.Default.GetBytes(str);
129return Convert.ToBase64String(barray);
130}
131
132/// <summary>
133/// 将Base64字符串解码为普通字符串
134/// </summary>
135/// <param name="str"/>要解码的字符串
136private string Base64Decode(string str)
137{
138byte[] barray;
139barray=Convert.FromBase64String(str);
140return Encoding.Default.GetString(barray);
141}
142
143/// <summary>
144/// 得到上传附件的文件流
145/// </summary>
146/// <param name="FilePath"/>附件的绝对路径
147private string GetStream(string FilePath)
148{
149//建立文件流对象
150System.IO.FileStream FileStr=new System.IO.FileStream(FilePath,System.IO.FileMode.Open);
151byte[] by=new byte[System.Convert.ToInt32(FileStr.Length)];
152FileStr.Read(by,0,by.Length);
153FileStr.Close();
154return(System.Convert.ToBase64String(by));
155}
156
157/// <summary>
158/// SMTP回应代码哈希表
159/// </summary>
160private void SMTPCodeAdd()
161{
162//[RFC 821 4.2.1.]
163/*
1644.2.2. NUMERIC ORDER LIST OF REPLY CODES
165
166211 System status, or system help reply
167214 Help message
168[Information on how to use the receiver or the meaning of a
169particular non-standard command; this reply is useful only
170to the human user]
171220 <domain> Service ready
172221 <domain> Service closing transmission channel
173250 Requested mail action okay, completed
174251 User not local; will forward to <forward-path>
175
176354 Start mail input; end with <crlf>.<crlf>
177
178421 <domain> Service not available,
179closing transmission channel
180[This may be a reply to any command if the service knows it
181must shut down]
182450 Requested mail action not taken: mailbox unavailable
183[E.g., mailbox busy]
184451 Requested action aborted: local error in processing
185452 Requested action not taken: insufficient system storage
186
187500 Syntax error, command unrecognized
188[This may include errors such as command line too long]
189501 Syntax error in parameters or arguments
190502 Command not implemented
191503 Bad sequence of commands
192504 Command parameter not implemented
193550 Requested action not taken: mailbox unavailable
194[E.g., mailbox not found, no access]
195551 User not local; please try <forward-path>
196552 Requested mail action aborted: exceeded storage allocation
197553 Requested action not taken: mailbox name not allowed
198[E.g., mailbox syntax incorrect]
199554 Transaction failed
200
201*/
202
203ErrCodeHT.Add("421","服务未就绪,关闭传输信道");
204ErrCodeHT.Add("432","需要一个密码转换");
205ErrCodeHT.Add("450","要求的邮件操作未完成,邮箱不可用(例如,邮箱忙)");
206ErrCodeHT.Add("451","放弃要求的操作;处理过程中出错");
207ErrCodeHT.Add("452","系统存储不足,要求的操作未执行");
208ErrCodeHT.Add("454","临时认证失败");
209ErrCodeHT.Add("500","邮箱地址错误");
210ErrCodeHT.Add("501","参数格式错误");
211ErrCodeHT.Add("502","命令不可实现");
212ErrCodeHT.Add("503","服务器需要SMTP验证");
213ErrCodeHT.Add("504","命令参数不可实现");
214ErrCodeHT.Add("530","需要认证");
215ErrCodeHT.Add("534","认证机制过于简单");
216ErrCodeHT.Add("538","当前请求的认证机制需要加密");
217ErrCodeHT.Add("550","要求的邮件操作未完成,邮箱不可用(例如,邮箱未找到,或不可访问)");
218ErrCodeHT.Add("551","用户非本地,请尝试<forward-path>");
219ErrCodeHT.Add("552","过量的存储分配,要求的操作未执行");
220ErrCodeHT.Add("553","邮箱名不可用,要求的操作未执行(例如邮箱格式错误)");
221ErrCodeHT.Add("554","传输失败");
222
223
224/*
225211 System status, or system help reply
226214 Help message
227[Information on how to use the receiver or the meaning of a
228particular non-standard command; this reply is useful only
229to the human user]
230220 <domain> Service ready
231221 <domain> Service closing transmission channel
232250 Requested mail action okay, completed
233251 User not local; will forward to <forward-path>
234
235354 Start mail input; end with <crlf>.<crlf>
236*/
237
238RightCodeHT.Add("220","服务就绪");
239RightCodeHT.Add("221","服务关闭传输信道");
240RightCodeHT.Add("235","验证成功");
241RightCodeHT.Add("250","要求的邮件操作完成");
242RightCodeHT.Add("251","非本地用户,将转发向<forward-path>");
243RightCodeHT.Add("334","服务器响应验证Base64字符串");
244RightCodeHT.Add("354","开始邮件输入,以<crlf>.<crlf>结束");
245
246}
247
248/// <summary>
249/// 发送SMTP命令
250/// </summary>
251private bool SendCommand(string str)
252{
253byte[]WriteBuffer;
254if(str==null||str.Trim()==String.Empty)
255{
256return true;
257}
258logs+=str;
259WriteBuffer = Encoding.Default.GetBytes(str);
260try
261{
262networkStream.Write(WriteBuffer,0,WriteBuffer.Length);
263}
264catch
265{
266errmsg="网络连接错误";
267return false;
268}
269return true;
270}
271
272/// <summary>
273/// 接收SMTP服务器回应
274/// </summary>
275private string RecvResponse()
276{
277int StreamSize;
278string Returnvalue = String.Empty;
279byte[] ReadBuffer = new byte[1024] ;
280try
281{
282StreamSize=networkStream.Read(ReadBuffer,0,ReadBuffer.Length);
283}
284catch
285{
286errmsg="网络连接错误";
287return "false";
288}
289
290if (StreamSize==0)
291{
292return Returnvalue ;
293}
294else
295{
296Returnvalue = Encoding.Default.GetString(ReadBuffer).Substring(0,StreamSize);
297logs+=Returnvalue+this.CRLF;
298return Returnvalue;
299}
300}
301
302/// <summary>
303/// 与服务器交互,发送一条命令并接收回应。
304/// </summary>
305/// <param name="str"/>一个要发送的命令
306/// <param name="errstr"/>如果错误,要反馈的信息
307private bool Dialog(string str,string errstr)
308{
309if(str==null||str.Trim()==string.Empty)
310{
311return true;
312}
313if(SendCommand(str))
314{
315string RR=RecvResponse();
316if(RR=="false")
317{
318return false;
319}
320//检查返回的代码,根据[RFC 821]返回代码为3位数字代码如220
321string RRCode=RR.Substring(0,3);
322if(RightCodeHT[RRCode]!=null)
323{
324return true;
325}
326else
327{
328if(ErrCodeHT[RRCode]!=null)
329{
330errmsg+=(RRCode+ErrCodeHT[RRCode].ToString());
331errmsg+=CRLF;
332}
333else
334{
335errmsg+=RR;
336}
337errmsg+=errstr;
338return false;
339}
340}
341else
342{
343return false;
344}
345}
346
347
348/// <summary>
349/// 与服务器交互,发送一组命令并接收回应。
350/// </summary>
351
352private bool Dialog(string[] str,string errstr)
353{
354for(int i=0;i<str.length;i++) (mailpriority="MailPriority.High)" ;="" <summary="" bool="" catch(exception="" connect(string="" e)="" else="" errmsg="网络连接失败" errmsg+="errstr;" false;="" getprioritystring(mailpriority="" if="" if(!dialog(str[i],""))="" if(rightcodeht[recvresponse().substring(0,3)]="null)" mailpriority)="" networkstream="tcpClient.GetStream();" port)="" priority="High" priority;="" private="" return="" smtpserver,int="" string="" tcpclient="new" tcpclient(smtpserver,port);="" true;="" try="" {="" }="" 创建tcp连接="" 连接服务器="" 验证网络连接是否正确="">
355/// 发送电子邮件,SMTP服务器不需要身份验证
356///
357/// <param name="smtpServer"/>
358/// <param name="port"/>
359/// <param name="mailMessage"/>
360/// <returns></returns>
361public bool SendEmail(string smtpServer,int port,MailMessage mailMessage)
362{
363return SendEmail(smtpServer,port,false,"","",mailMessage);
364}
365
366/// <summary>
367/// 发送电子邮件,SMTP服务器需要身份验证
368/// </summary>
369/// <param name="smtpServer"/>
370/// <param name="port"/>
371/// <param name="username"/>
372/// <param name="password"/>
373/// <param name="mailMessage"/>
374/// <returns></returns>
375public bool SendEmail(string smtpServer,int port,string username,string password,MailMessage mailMessage)
376{
377return SendEmail(smtpServer,port,false,username,password,mailMessage);
378}
379
380private bool SendEmail(string smtpServer,int port,bool ESmtp,string username,string password,MailMessage mailMessage)
381{
382if (Connect(smtpServer,port)==false)//测试连接服务器是否成功
383return false;
384
385string priority=GetPriorityString(mailMessage.Priority);
386bool Html=(mailMessage.BodyFormat==MailFormat.HTML);
387
388string[] SendBuffer;
389string SendBufferstr;
390
391//进行SMTP验证,现在大部分SMTP服务器都要认证
392if(ESmtp)
393{
394SendBuffer=new String[4];
395SendBuffer[0]="EHLO " + smtpServer + CRLF;
396SendBuffer[1]="AUTH LOGIN" + CRLF;
397SendBuffer[2]=Base64Encode(username) + CRLF;
398SendBuffer[3]=Base64Encode(password) + CRLF;
399if(!Dialog(SendBuffer,"SMTP服务器验证失败,请核对用户名和密码。"))
400return false;
401}
402else
403{//不需要身份认证
404SendBufferstr="HELO " + smtpServer + CRLF;
405if(!Dialog(SendBufferstr,""))
406return false;
407}
408
409//发件人地址
410SendBufferstr="MAIL FROM:<" + mailMessage.From + ">" + CRLF;
411if(!Dialog(SendBufferstr,"发件人地址错误,或不能为空"))
412return false;
413
414//收件人地址
415SendBuffer=new string[mailMessage.Recipients.Count];
416for(int i=0;i<mailmessage.recipients.count;i++) +"="" +(string)mailmessage.recipients[i]="" sendbuffer[i]="RCPT TO:<" {="">" + CRLF;
417}
418if(!Dialog(SendBuffer,"收件人地址有误"))
419return false;
420
421/*
422SendBuffer=new string[10];
423for(int i=0;i<recipientbcc.count;i++) +="" +"="" recipientbcc[i].tostring()="" sendbuffer[i]="RCPT TO:<" {="">" + CRLF;
424
425}
426
427if(!Dialog(SendBuffer,"密件收件人地址有误"))
428return false;
429*/
430
431SendBufferstr="DATA" + CRLF;
432if(!Dialog(SendBufferstr,""))
433return false;
434
435//发件人姓名
436SendBufferstr="From:" + mailMessage.FromName + "<" +mailMessage.From +">" +CRLF;
437
438//if(ReplyTo.Trim()!="")
439//{
440// SendBufferstr+="Reply-To: " + ReplyTo + CRLF;
441//}
442
443//SendBufferstr+="To:" + ToName + "<" + Recipient[0] +">" +CRLF;
444//至少要有一个收件人
445if (mailMessage.Recipients.Count==0)
446{
447return false;
448}
449else
450{
451SendBufferstr += "To:=?"+mailMessage.Charset.ToUpper()+"?B?"+
452Base64Encode((string)mailMessage.Recipients[0])+"?="+"<"+(string)mailMessage.Recipients[0]+">"+CRLF;
453}
454
455//SendBufferstr+="CC:";
456//for(int i=0;i<recipient.count;i++) "<"="" +="" +"="" recipient[i].tostring()="" sendbufferstr+="Recipient[i].ToString()" {="">,";
457//}
458//SendBufferstr+=CRLF;
459
460SendBufferstr+=
461((mailMessage.Subject==String.Empty || mailMessage.Subject==null)?"Subject:":((mailMessage.Charset=="")?("Subject:" +
462mailMessage.Subject):("Subject:" + "=?" + mailMessage.Charset.ToUpper() + "?B?" +
463Base64Encode(mailMessage.Subject) +"?="))) + CRLF;
464SendBufferstr+="X-Priority:" + priority + CRLF;
465SendBufferstr+="X-MSMail-Priority:" + priority + CRLF;
466SendBufferstr+="Importance:" + priority + CRLF;
467SendBufferstr+="X-Mailer: Lion.Web.Mail.SmtpMail Pubclass [cn]" + CRLF;
468SendBufferstr+="MIME-Version: 1.0" + CRLF;
469if(mailMessage.Attachments.Count!=0)
470{
471SendBufferstr+="Content-Type: multipart/mixed;" + CRLF;
472SendBufferstr += " boundary=\"====="+
473(Html?"001_Dragon520636771063_":"001_Dragon303406132050_")+"=====\""+CRLF+CRLF;
474}
475
476if(Html)
477{
478if(mailMessage.Attachments.Count==0)
479{
480SendBufferstr += "Content-Type: multipart/alternative;"+CRLF;//内容格式和分隔符
481SendBufferstr += " boundary=\"=====003_Dragon520636771063_=====\""+CRLF+CRLF;
482SendBufferstr += "This is a multi-part message in MIME format."+CRLF+CRLF;
483}
484else
485{
486SendBufferstr +="This is a multi-part message in MIME format."+CRLF+CRLF;
487SendBufferstr += "--=====001_Dragon520636771063_====="+CRLF;
488SendBufferstr += "Content-Type: multipart/alternative;"+CRLF;//内容格式和分隔符
489SendBufferstr += " boundary=\"=====003_Dragon520636771063_=====\""+CRLF+CRLF;
490}
491SendBufferstr += "--=====003_Dragon520636771063_====="+CRLF;
492SendBufferstr += "Content-Type: text/plain;"+ CRLF;
493SendBufferstr += ((mailMessage.Charset=="")?(" charset=\"iso-8859-1\""):(" charset=\"" +
494
495mailMessage.Charset.ToLower() + "\"")) + CRLF;
496SendBufferstr+="Content-Transfer-Encoding: base64" + CRLF + CRLF;
497SendBufferstr+= Base64Encode("邮件内容为HTML格式,请选择HTML方式查看") + CRLF + CRLF;
498
499SendBufferstr += "--=====003_Dragon520636771063_====="+CRLF;
500
501
502SendBufferstr+="Content-Type: text/html;" + CRLF;
503SendBufferstr+=((mailMessage.Charset=="")?(" charset=\"iso-8859-1\""):(" charset=\"" +
504mailMessage.Charset.ToLower() + "\"")) + CRLF;
505SendBufferstr+="Content-Transfer-Encoding: base64" + CRLF + CRLF;
506SendBufferstr+= Base64Encode(mailMessage.Body) + CRLF + CRLF;
507SendBufferstr += "--=====003_Dragon520636771063_=====--"+CRLF;
508}
509else
510{
511if(mailMessage.Attachments.Count!=0)
512{
513SendBufferstr += "--=====001_Dragon303406132050_====="+CRLF;
514}
515SendBufferstr+="Content-Type: text/plain;" + CRLF;
516SendBufferstr+=((mailMessage.Charset=="")?(" charset=\"iso-8859-1\""):(" charset=\"" +
517mailMessage.Charset.ToLower() + "\"")) + CRLF;
518SendBufferstr+="Content-Transfer-Encoding: base64" + CRLF + CRLF;
519SendBufferstr+= Base64Encode(mailMessage.Body) + CRLF;
520}
521
522//SendBufferstr += "Content-Transfer-Encoding: base64"+CRLF;
523
524if(mailMessage.Attachments.Count!=0)
525{
526for(int i=0;i<mailmessage.attachments.count;i++) "."="" (html?"001_dragon520636771063_":"001_dragon303406132050_")="" (html?"001_dragon520636771063_":"001_dragon303406132050_")+"='--"+CRLF+CRLF;' +="" +"="+CRLF;
527//SendBufferstr += " +crlf;="" ;="" <summary="" =?"+mailmessage.charset.toupper()+"?b?"+="" \\\")+1))+"?='\""+CRLF+CRLF' _smtpserver;="" application="" base64encode(filepath.substring(filepath.lastindexof("="" class="" content-type:="" crlf;="" false;="" filepath="(string)mailMessage.Attachments[i];" if(!dialog(sendbufferstr,"断开连接时错误"))="" if(!dialog(sendbufferstr,"错误信件信息"))="" networkstream.close();="" octet-stream"+crlf;="" private="" public="" return="" sendbufferstr="QUIT" smtpmail="" static="" string="" tcpclient.close();="" true;="" {="" }="" 内容结束="">
528/// 格式:SmtpAccount:Password@SmtpServerAddress<br/>
529/// 或者:SmtpServerAddress<br/>
530/// <code>
531/// SmtpMail.SmtpServer="user:[email protected]";
532/// //或者:
533/// SmtpMail.SmtpServer="smtp.126.com";
534/// 或者:
535/// SmtpMail.SmtpServer=SmtpServerHelper.GetSmtpServer("user","12345678","smtp.126.com");
536/// </code>
537///
538public static string SmtpServer
539{
540set { _SmtpServer=value;}
541get { return _SmtpServer;}
542}
543
544public static bool Send(MailMessage mailMessage,string username,string password)
545{
546SmtpServerHelper helper=new SmtpServerHelper();
547return helper.SendEmail(_SmtpServer,25,username,password,mailMessage);
548}
549
550}
551
552#endregion
553}
554
555
556
557
558using System;
559using NUnit.Framework;
560
561
562namespace SkyDev.Web.Mail
563{
564/// <summary>
565/// Test 的摘要说明。
566/// </summary>
567[TestFixture]
568public class TestSmtpMail
569{
570//安装测试用例,完成初始化操作
571[SetUp]
572public void SetUp()
573{
574}
575
576//测试结束完成清理操作
577[TearDown]
578public void TearDown()
579{
580
581}
582
583[Test]
584public void TestMailAttachments()
585{
586SkyDev.Web.Mail.MailAttachments attachments=new MailAttachments();
587Assert.AreEqual(0,attachments.Count,"初始化MailAttachments");
588attachments.Add("c:\\\autoexec.bat");
589Assert.AreEqual(1,attachments.Count,"增加附件(附件确实存在)");
590attachments.Add("c:\\\autoexec.dat.txt");
591Assert.AreEqual(1,attachments.Count,"增加附件(附件不存在)");
592attachments.Clear();
593Assert.AreEqual(0,attachments.Count,"清除附件");
594}
595
596[Test]
597public void TestMailMessage()
598{
599MailMessage message=new MailMessage();
600Assert.AreEqual(0,message.Attachments.Count,"初始化MailAttachments");
601Assert.AreEqual(MailFormat.Text,message.BodyFormat,"邮件格式");
602Assert.AreEqual("GB2312",message.Charset,"缺省的字符集");
603}
604
605[Test]
606public void TestSendMail()
607{
608SmtpMail.SmtpServer="smtp.126.com";
609MailMessage mail=new MailMessage();
610mail.From=" [email protected] ";
611mail.FromName="曾青松";
612mail.AddRecipients(" [email protected] ");
613mail.Subject="主题:测试邮件";
614mail.BodyFormat=MailFormat.Text;
615mail.Body="测试的内容.";
616mail.Attachments.Add("c:\\\test.txt");
617SmtpMail.Send(mail,"","");//请填写自己的测试邮件帐号
618}
619}
620}</mailmessage.attachments.count;i++)></recipient.count;i++)></recipientbcc.count;i++)></mailmessage.recipients.count;i++)></str.length;i++)></crlf></crlf></forward-path></crlf></crlf></forward-path></domain></domain></forward-path></forward-path></domain></crlf></crlf></forward-path></domain></domain></maxrecipientnum)></maxattachmentnum)></filepath.length;i++)>