EmailHelper.cs 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
  1. using ProjectBase.Data.Logs;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. using System.Text;
  10. using System.Text.RegularExpressions;
  11. using System.Threading.Tasks;
  12. namespace ProjectBase.Data.Email
  13. {
  14. /// <summary>
  15. /// 发送邮件的辅助类,可以发送附件、嵌入图片、HTML等内容邮件。使用底层SMTP协议指令进行发送。
  16. /// </summary>
  17. public class EmailHelper
  18. {
  19. #region public属性字段
  20. /// <summary>
  21. /// 设定语言代码,默认设定为GB2312,如不需要可设置为""
  22. /// </summary>
  23. public string Charset = "GB2312";
  24. private NetworkStream StrmWtr;
  25. private StreamReader StrmRdr;
  26. private String cmdData;
  27. private byte[] szData;
  28. private const String CRLF = "\r\n";
  29. /// <summary>
  30. /// 邮箱服务器
  31. /// </summary>
  32. public string MailServer
  33. {
  34. get { return mailserver; }
  35. set { mailserver = value; }
  36. }
  37. /// <summary>
  38. /// 邮件服务器端口号,默认端口为25
  39. /// </summary>
  40. public int MailServerPort
  41. {
  42. set
  43. {
  44. mailserverport = value;
  45. }
  46. get
  47. {
  48. return mailserverport;
  49. }
  50. }
  51. /// <summary>
  52. /// SMTP认证时使用的用户名
  53. /// </summary>
  54. public string MailServerUsername
  55. {
  56. set
  57. {
  58. if (value.Trim() != "")
  59. {
  60. username = value.Trim();
  61. useSmtpAuth = true;
  62. }
  63. else
  64. {
  65. username = "";
  66. useSmtpAuth = false;
  67. }
  68. }
  69. get
  70. {
  71. return username;
  72. }
  73. }
  74. /// <summary>
  75. /// SMTP认证时使用的密码
  76. /// </summary>
  77. public string MailServerPassword
  78. {
  79. set
  80. {
  81. password = value;
  82. }
  83. get
  84. {
  85. return password;
  86. }
  87. }
  88. /// <summary>
  89. /// 发件人地址
  90. /// </summary>
  91. public string From
  92. {
  93. get { return from; }
  94. set
  95. {
  96. from = value;
  97. //如果未设置fromName,则fromName使用发件人邮箱
  98. if (string.IsNullOrEmpty(fromName))
  99. {
  100. fromName = from;
  101. }
  102. }
  103. }
  104. /// <summary>
  105. /// 发件人姓名
  106. /// </summary>
  107. public string FromName
  108. {
  109. get { return fromName; }
  110. set { fromName = value; }
  111. }
  112. /// <summary>
  113. /// 回复邮件地址
  114. /// </summary>
  115. public string ReplyTo = "";
  116. /// <summary>
  117. /// 邮件主题
  118. /// </summary>
  119. public string Subject = "";
  120. /// <summary>
  121. /// 是否Html邮件
  122. /// </summary>
  123. public bool IsHtml = false;
  124. /// <summary>
  125. /// 收件人是否发送收条
  126. /// </summary>
  127. public bool ReturnReceipt = false;
  128. /// <summary>
  129. /// 邮件正文
  130. /// </summary>
  131. public string Body = "";
  132. /// <summary>
  133. /// 邮件发送优先级,可设置为"High","Normal","Low"或"1","3","5"
  134. /// </summary>
  135. public string Priority
  136. {
  137. set
  138. {
  139. switch (value.ToLower())
  140. {
  141. case "high":
  142. priority = "High";
  143. break;
  144. case "1":
  145. priority = "High";
  146. break;
  147. case "normal":
  148. priority = "Normal";
  149. break;
  150. case "3":
  151. priority = "Normal";
  152. break;
  153. case "low":
  154. priority = "Low";
  155. break;
  156. case "5":
  157. priority = "Low";
  158. break;
  159. default:
  160. priority = "Normal";
  161. break;
  162. }
  163. }
  164. }
  165. /// <summary>
  166. /// 错误消息反馈
  167. /// </summary>
  168. public string ErrorMessage
  169. {
  170. get
  171. {
  172. return errmsg;
  173. }
  174. }
  175. /// <summary>
  176. /// 收件人姓名
  177. /// </summary>
  178. public string RecipientName = "";
  179. #endregion
  180. #region private属性字段
  181. /// <summary>
  182. /// 邮件服务器域名
  183. /// </summary>
  184. private string mailserver;
  185. /// <summary>
  186. /// 邮件服务器端口号
  187. /// </summary>
  188. private int mailserverport = 995;
  189. /// <summary>
  190. /// 发件人地址
  191. /// </summary>
  192. private string from = "";
  193. /// <summary>
  194. /// 发件人姓名
  195. /// </summary>
  196. private string fromName = "";
  197. /// <summary>
  198. /// 是否需要SMTP验证
  199. /// </summary>
  200. private bool useSmtpAuth = true;
  201. /// <summary>
  202. /// 是否需要Pop3验证
  203. /// </summary>
  204. private bool usePop3Auth = false;
  205. /// <summary>
  206. /// SMTP认证时使用的用户名
  207. /// </summary>
  208. private string username = "";
  209. /// <summary>
  210. /// SMTP认证时使用的密码
  211. /// </summary>
  212. private string password = "";
  213. /// <summary>
  214. /// 收件人最大数量:现在很多SMTP都限制收件人的最大数量,以防止广告邮件泛滥,最大数量一般都限制在10个以下。
  215. /// </summary>
  216. private int RecipientMaxNum = 10;
  217. /// <summary>
  218. /// 收件人列表
  219. /// </summary>
  220. private ArrayList Recipient = new ArrayList();
  221. /// <summary>
  222. ///抄送收件人列表
  223. /// </summary>
  224. private ArrayList RecipientCC = new ArrayList();
  225. /// <summary>
  226. /// 密送收件人列表
  227. /// </summary>
  228. private ArrayList RecipientBCC = new ArrayList();
  229. /// <summary>
  230. /// 邮件发送优先级,可设置为"High","Normal","Low"或"1","3","5"
  231. /// </summary>
  232. private string priority = "Normal";
  233. /// <summary>
  234. /// 错误消息反馈
  235. /// </summary>
  236. private string errmsg;
  237. /// <summary>
  238. /// 回车换行
  239. /// </summary>
  240. private string enter = "\r\n";
  241. /// <summary>
  242. /// TcpClient对象,用于连接服务器
  243. /// </summary>
  244. private TcpClient tc;
  245. /// <summary>
  246. /// NetworkStream对象
  247. /// </summary>
  248. private NetworkStream ns;
  249. /// <summary>
  250. /// SMTP错误代码哈希表
  251. /// </summary>
  252. private Hashtable ErrCodeHT = new Hashtable();
  253. /// <summary>
  254. /// SMTP正确代码哈希表
  255. /// </summary>
  256. private Hashtable RightCodeHT = new Hashtable();
  257. #endregion
  258. /// <summary>
  259. /// SMTP回应代码哈希表
  260. /// </summary>
  261. private void SMTPCodeAdd()
  262. {
  263. ErrCodeHT.Add("500", "邮箱地址错误");
  264. ErrCodeHT.Add("501", "参数格式错误");
  265. ErrCodeHT.Add("502", "命令不可实现");
  266. ErrCodeHT.Add("503", "服务器需要SMTP验证");
  267. ErrCodeHT.Add("504", "命令参数不可实现");
  268. ErrCodeHT.Add("421", "服务未就绪,关闭传输信道");
  269. ErrCodeHT.Add("450", "要求的邮件操作未完成,邮箱不可用(例如,邮箱忙)");
  270. ErrCodeHT.Add("550", "要求的邮件操作未完成,邮箱不可用(例如,邮箱未找到,或不可访问)");
  271. ErrCodeHT.Add("451", "放弃要求的操作;处理过程中出错");
  272. ErrCodeHT.Add("551", "用户非本地,请尝试<forward-path>");
  273. ErrCodeHT.Add("452", "系统存储不足,要求的操作未执行");
  274. ErrCodeHT.Add("552", "过量的存储分配,要求的操作未执行");
  275. ErrCodeHT.Add("553", "邮箱名不可用,要求的操作未执行(例如邮箱格式错误)");
  276. ErrCodeHT.Add("432", "需要一个密码转换");
  277. ErrCodeHT.Add("534", "认证机制过于简单");
  278. ErrCodeHT.Add("538", "当前请求的认证机制需要加密");
  279. ErrCodeHT.Add("454", "临时认证失败");
  280. ErrCodeHT.Add("530", "需要认证");
  281. RightCodeHT.Add("220", "服务就绪");
  282. RightCodeHT.Add("250", "要求的邮件操作完成");
  283. RightCodeHT.Add("251", "用户非本地,将转发向<forward-path>");
  284. RightCodeHT.Add("354", "开始邮件输入,以<CRLF>.<CRLF>结束");
  285. RightCodeHT.Add("221", "服务关闭传输信道");
  286. RightCodeHT.Add("334", "服务器响应验证Base64字符串");
  287. RightCodeHT.Add("235", "验证成功");
  288. }
  289. /// <summary>
  290. /// 默认构造函数
  291. /// </summary>
  292. public EmailHelper()
  293. {
  294. SMTPCodeAdd();
  295. }
  296. /// <summary>
  297. /// 待邮箱发送配置参数的构造函数
  298. /// </summary>
  299. /// <param name="mailServer">邮件服务器</param>
  300. /// <param name="username">用户名</param>
  301. /// <param name="password">用户密码</param>
  302. public EmailHelper(string mailServer, string username, string password) :
  303. this(mailServer, username, password, 25)
  304. {
  305. }
  306. /// <summary>
  307. /// 待邮箱发送配置参数的构造函数
  308. /// </summary>
  309. /// <param name="mailServer">邮件服务器</param>
  310. /// <param name="username">用户名</param>
  311. /// <param name="password">用户密码</param>
  312. /// <param name="port">邮箱服务器端口</param>
  313. public EmailHelper(string mailServer, string username, string password, int port)
  314. {
  315. this.MailServer = mailServer;
  316. this.MailServerUsername = username;
  317. this.MailServerPassword = password;
  318. this.MailServerPort = port;
  319. SMTPCodeAdd();
  320. }
  321. /// <summary>
  322. /// 析构函数
  323. /// </summary>
  324. ~EmailHelper()
  325. {
  326. if (ns != null)
  327. {
  328. ns.Close();
  329. }
  330. if (tc != null)
  331. {
  332. tc.Close();
  333. }
  334. }
  335. #region 附件
  336. /// <summary>
  337. /// 添加一个附件,需使用绝对路径
  338. /// </summary>
  339. public bool AddAttachment(string path)
  340. {
  341. if (File.Exists(path))
  342. {
  343. Attachments.Add(path);
  344. return true;
  345. }
  346. else
  347. {
  348. errmsg += "要附加的文件不存在" + enter;
  349. return false;
  350. }
  351. }
  352. /// <summary>
  353. /// 用于分割附件的分割符.
  354. /// </summary>
  355. private string boundary = "=====000_HuolxPubClass113273537350_=====";
  356. /// <summary>
  357. /// 分隔符
  358. /// </summary>
  359. private string boundary1 = "=====001_HuolxPubClass113273537350_=====";
  360. /// <summary>
  361. /// 用于存放附件路径的信息
  362. /// </summary>
  363. private List<string> Attachments = new List<string>();
  364. /// <summary>
  365. /// 附件的BASE64编码字符串
  366. /// </summary>
  367. /// <param name="path">附件路径</param>
  368. private string AttachmentB64Str(string path)
  369. {
  370. FileStream fs;
  371. try
  372. {
  373. fs = new FileStream(path, System.IO.FileMode.Open, FileAccess.Read, FileShare.Read);
  374. }
  375. catch (Exception ex)
  376. {
  377. errmsg += "要附加的文件不存在" + enter;
  378. LogHelper.log.Error(errmsg, ex);
  379. return Base64Encode("要附加的文件:" + path + "不存在");
  380. }
  381. int fl = (int)fs.Length;
  382. byte[] barray = new byte[fl];
  383. fs.Read(barray, 0, fl);
  384. fs.Close();
  385. return B64StrLine(Convert.ToBase64String(barray));
  386. }
  387. /// <summary>
  388. /// 如果文件名中含有非英文字母,则将其编码
  389. /// </summary>
  390. private string AttachmentNameStr(string fn)
  391. {
  392. if (Encoding.Default.GetByteCount(fn) > fn.Length)
  393. {
  394. return "=?" + Charset.ToUpper() + "?B?" + Base64Encode(fn) + "?=";
  395. }
  396. else
  397. {
  398. return fn;
  399. }
  400. }
  401. private string B64StrLine(string str)
  402. {
  403. StringBuilder B64sb = new StringBuilder(str);
  404. for (int i = 76; i < B64sb.Length; i += 78)
  405. {
  406. B64sb.Insert(i, enter);
  407. }
  408. return B64sb.ToString();
  409. }
  410. /// <summary>
  411. /// 将字符串编码为Base64字符串
  412. /// </summary>
  413. /// <param name="str">要编码的字符串</param>
  414. private string Base64Encode(string str)
  415. {
  416. byte[] barray;
  417. barray = Encoding.Default.GetBytes(str);
  418. return Convert.ToBase64String(barray);
  419. }
  420. /// <summary>
  421. /// 将Base64字符串解码为普通字符串
  422. /// </summary>
  423. /// <param name="dstr">要解码的字符串</param>
  424. private string Base64Decode(string dstr)
  425. {
  426. byte[] barray;
  427. barray = Convert.FromBase64String(dstr);
  428. return Encoding.Default.GetString(barray);
  429. }
  430. #endregion
  431. #region 嵌入图片处理
  432. private Hashtable EmbedList = new Hashtable(); //widened scope for MatchEvaluator
  433. /// <summary>
  434. /// 修改HTML页面中的图片引用为嵌入式图片邮件内容
  435. /// </summary>
  436. /// <param name="rawHtml">原始HTML内容</param>
  437. /// <param name="extras"></param>
  438. /// <param name="boundaryString"></param>
  439. /// <returns></returns>
  440. private string FixupReferences(string rawHtml, ref StringBuilder extras, string boundaryString)
  441. {
  442. //Build a symbol table to avoid redundant embedding.
  443. Regex imgRE, linkRE, hrefRE;
  444. MatchCollection imgMatches;
  445. //图片查找正则表达式
  446. string imgMatchExpression = @"(?<=img+.+src\=[\x27\x22])(?<Url>[^\x27\x22]*)(?=[\x27\x22])";
  447. imgRE = new Regex(imgMatchExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
  448. //Link内容查找正则表达式
  449. string linkMatchExpression = "<\\s*link[^>]+href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>\\S+))[^>]*>";
  450. linkRE = new Regex(linkMatchExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
  451. //修正页面内相对URL地址表达式
  452. string refMatchExpression = "href\\s*=\\s*(?:['\"](?<1>[^\"]*)['\"]|(?<1>\\S+))";
  453. hrefRE = new Regex(refMatchExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
  454. imgMatches = imgRE.Matches(rawHtml);
  455. foreach (Match m in imgMatches)
  456. {
  457. if (!EmbedList.ContainsKey(m.Groups[1].Value))
  458. {
  459. EmbedList.Add(m.Groups[1].Value, Guid.NewGuid());
  460. }
  461. }
  462. //准备嵌入数据
  463. extras.Length = 0;
  464. string contentType;
  465. ArrayList embeddees = new ArrayList(EmbedList.Keys);
  466. foreach (string embeddee in embeddees)
  467. {
  468. contentType = embeddee.Substring(embeddee.LastIndexOf(".") + 1).ToLower();
  469. extras.AppendFormat(boundaryString);
  470. if (contentType.Equals("jpg")) contentType = "jpeg";
  471. switch (contentType)
  472. {
  473. case "jpeg":
  474. case "gif":
  475. case "png":
  476. case "bmp":
  477. extras.AppendFormat("Content-Type: image/{0}; charset=\"iso-8859-1\"\r\n", contentType);
  478. extras.Append("Content-Transfer-Encoding: base64\r\n");
  479. extras.Append("Content-Disposition: inline\r\n");
  480. extras.AppendFormat("Content-ID: <{0}>\r\n\r\n", EmbedList[embeddee]);
  481. extras.Append(GetDataAsBase64(embeddee));
  482. extras.Append("\r\n");
  483. break;
  484. }
  485. }
  486. //Fixups for references to items now embedded
  487. rawHtml = imgRE.Replace(rawHtml, new MatchEvaluator(FixupEmbedPath));
  488. return rawHtml;
  489. }
  490. /// <summary>
  491. /// 修正嵌入图片的地址应用为cid:***
  492. /// </summary>
  493. private string FixupEmbedPath(Match m)
  494. {
  495. string replaceThis = m.Groups[1].Value;
  496. string withThis = string.Format("cid:{0}", EmbedList[replaceThis]);
  497. return m.Value.Replace(replaceThis, withThis);
  498. }
  499. private string GetDataAsBase64(string sUrl)
  500. {
  501. WebClient webClient = new WebClient();
  502. webClient.Headers.Add("user-agent", "Mozilla/4.0 (compatible;MSIE 6.0)");
  503. MemoryStream memoryStream = new MemoryStream();
  504. Stream stream = webClient.OpenRead(sUrl);
  505. byte[] chunk = new byte[4096];
  506. int cbChunk;
  507. while ((cbChunk = stream.Read(chunk, 0, 4096)) > 0)
  508. memoryStream.Write(chunk, 0, cbChunk);
  509. stream.Close();
  510. byte[] buf = new byte[memoryStream.Length];
  511. memoryStream.Position = 0;
  512. memoryStream.Read(buf, 0, (int)memoryStream.Length);
  513. memoryStream.Close();
  514. string b64 = Convert.ToBase64String(buf);
  515. StringBuilder base64 = new StringBuilder();
  516. int i;
  517. for (i = 0; i + 60 < b64.Length; i += 60)
  518. base64.AppendFormat("{0}\r\n", b64.Substring(i, 60));
  519. base64.Append(b64.Substring(i));
  520. for (i = 0; i < (60 - (b64.Length % 60)); i++) base64.Append('=');
  521. base64.Append("\r\n");
  522. return base64.ToString();
  523. }
  524. private string GetDataAsString(string sUrl)
  525. {
  526. WebClient webClient = new WebClient();
  527. webClient.Headers.Add("user-agent", "Mozilla/4.0 (compatible;MSIE 6.0)");
  528. return (new StreamReader(webClient.OpenRead(sUrl))).ReadToEnd();
  529. }
  530. #endregion
  531. #region 收件人
  532. /// <summary>
  533. /// 添加一个收件人
  534. /// </summary>
  535. /// <param name="str">收件人地址</param>
  536. /// <param name="ra"></param>
  537. private bool AddRs(string str, ArrayList ra)
  538. {
  539. str = str.Trim();
  540. if (str == null || str == "" || str.IndexOf("@") == -1)
  541. {
  542. return true;
  543. // 上面的语句自动滤除无效的收件人,为了不影响正常运作,未返回错误,如果您需要严格的检查收件人,请替换为下面的语句。
  544. // errmsg+="存在无效收件人:" +str;
  545. // return false;
  546. }
  547. if (ra.Count < RecipientMaxNum)
  548. {
  549. ra.Add(str);
  550. return true;
  551. }
  552. else
  553. {
  554. errmsg += "收件人过多";
  555. return false;
  556. }
  557. }
  558. /// <summary>
  559. /// 添加一组收件人(不超过10个),参数为字符串数组
  560. /// </summary>
  561. /// <param name="str">保存有收件人地址的字符串数组(不超过10个)</param>
  562. /// <param name="ra">添加的数组列表</param>
  563. private bool AddRs(string[] str, ArrayList ra)
  564. {
  565. for (int i = 0; i < str.Length; i++)
  566. {
  567. if (!AddRs(str[i], ra))
  568. {
  569. return false;
  570. }
  571. }
  572. return true;
  573. }
  574. /// <summary>
  575. /// 添加一个收件人
  576. /// </summary>
  577. /// <param name="str">收件人地址</param>
  578. public bool AddRecipient(string str)
  579. {
  580. return AddRs(str, Recipient);
  581. }
  582. /// <summary>
  583. /// 指定一个收件人
  584. /// </summary>
  585. /// <param name="str">收件人地址</param>
  586. public void SetRecipient(string str)
  587. {
  588. //return AddRs(str, Recipient);
  589. Recipient.Clear();
  590. Recipient.Add(str);
  591. }
  592. /// <summary>
  593. /// 添加一组收件人(不超过10个),参数为字符串数组
  594. /// </summary>
  595. /// <param name="str">保存有收件人地址的字符串数组(不超过RecipientMaxNum个)</param>
  596. public bool AddRecipient(string[] str)
  597. {
  598. return AddRs(str, Recipient);
  599. }
  600. /// <summary>
  601. /// 添加一个抄送收件人
  602. /// </summary>
  603. /// <param name="str">收件人地址</param>
  604. public bool AddRecipientCC(string str)
  605. {
  606. return AddRs(str, RecipientCC);
  607. }
  608. /// <summary>
  609. /// 添加一组抄送收件人(不超过10个),参数为字符串数组
  610. /// </summary>
  611. /// <param name="str">保存有收件人地址的字符串数组(不超过RecipientMaxNum个)</param>
  612. public bool AddRecipientCC(string[] str)
  613. {
  614. return AddRs(str, RecipientCC);
  615. }
  616. /// <summary>
  617. /// 添加一个密件收件人
  618. /// </summary>
  619. /// <param name="str">收件人地址</param>
  620. public bool AddRecipientBCC(string str)
  621. {
  622. return AddRs(str, RecipientBCC);
  623. }
  624. /// <summary>
  625. /// 添加一组密件收件人(不超过10个),参数为字符串数组
  626. /// </summary>
  627. /// <param name="str">保存有收件人地址的字符串数组(不超过RecipientMaxNum个)</param>
  628. public bool AddRecipientBCC(string[] str)
  629. {
  630. return AddRs(str, RecipientBCC);
  631. }
  632. /// <summary>
  633. /// 清空收件人列表
  634. /// </summary>
  635. public void ClearRecipient()
  636. {
  637. Recipient.Clear();
  638. }
  639. #endregion
  640. #region 连接邮件服务器
  641. /// <summary>
  642. /// 发送SMTP命令
  643. /// </summary>
  644. private bool SendCommand(string Command)
  645. {
  646. byte[] WriteBuffer;
  647. if (Command == null || Command.Trim() == "")
  648. {
  649. return true;
  650. }
  651. //logs+=Command;
  652. WriteBuffer = Encoding.Default.GetBytes(Command);
  653. try
  654. {
  655. ns.Write(WriteBuffer, 0, WriteBuffer.Length);
  656. }
  657. catch
  658. {
  659. errmsg = "网络连接错误";
  660. return false;
  661. }
  662. return true;
  663. }
  664. /// <summary>
  665. /// 接收SMTP服务器回应
  666. /// </summary>
  667. private string RecvResponse()
  668. {
  669. int StreamSize;
  670. string ReturnValue = "false";
  671. byte[] ReadBuffer = new byte[4096];
  672. try
  673. {
  674. StreamSize = ns.Read(ReadBuffer, 0, ReadBuffer.Length);
  675. }
  676. catch
  677. {
  678. errmsg = "网络连接错误";
  679. return ReturnValue;
  680. }
  681. if (StreamSize == 0)
  682. {
  683. return ReturnValue;
  684. }
  685. else
  686. {
  687. ReturnValue = Encoding.Default.GetString(ReadBuffer).Substring(0, StreamSize).Trim(); ;
  688. //logs+=ReturnValue;
  689. return ReturnValue;
  690. }
  691. }
  692. /// <summary>
  693. /// 与服务器交互,发送一条命令并接收回应。
  694. /// </summary>
  695. /// <param name="Command">一个要发送的命令</param>
  696. /// <param name="errstr">如果错误,要反馈的信息</param>
  697. private bool Dialog(string Command, string errstr)
  698. {
  699. if (Command == null || Command.Trim() == "")
  700. {
  701. return true;
  702. }
  703. if (SendCommand(Command))
  704. {
  705. string RR = RecvResponse();
  706. if (RR == "false")
  707. {
  708. return false;
  709. }
  710. string RRCode = "";
  711. if (RR.Length >= 3)
  712. RRCode = RR.Substring(0, 3);
  713. else
  714. RRCode = RR;
  715. if (ErrCodeHT[RRCode] != null)
  716. {
  717. errmsg += (RRCode + ErrCodeHT[RRCode].ToString());
  718. errmsg += enter;
  719. return false;
  720. }
  721. else
  722. {
  723. return true;
  724. }
  725. }
  726. else
  727. {
  728. return false;
  729. }
  730. }
  731. /// <summary>
  732. /// 与服务器交互,发送一组命令并接收回应。
  733. /// </summary>
  734. private bool Dialog(ArrayList Command, string errstr)
  735. {
  736. foreach (String item in Command)
  737. {
  738. if (!Dialog(item, ""))
  739. {
  740. errmsg += enter;
  741. errmsg += errstr;
  742. return false;
  743. }
  744. }
  745. return true;
  746. }
  747. /// <summary>
  748. /// SMTP验证过程.
  749. /// </summary>
  750. private bool SmtpAuth()
  751. {
  752. ArrayList SendBuffer = new ArrayList();
  753. string SendBufferstr;
  754. SendBufferstr = "EHLO " + mailserver + enter;
  755. // SendBufferstr="HELO " + mailserver + enter;
  756. //这个地方经常出现命令错位,不得以加入特殊控制代码,才能正常执行。
  757. //以后最好能有更好的解决办法。
  758. if (SendCommand(SendBufferstr))
  759. {
  760. while (true)
  761. {
  762. int i = 0;
  763. if (ns.DataAvailable)
  764. {
  765. string RR = RecvResponse();
  766. if (RR == "false")
  767. {
  768. return false;
  769. }
  770. string RRCode = RR.Substring(0, 3);
  771. if (RightCodeHT[RRCode] != null)
  772. {
  773. if (RR.IndexOf("AUTH") != -1)
  774. {
  775. break;
  776. }
  777. }
  778. else
  779. {
  780. if (ErrCodeHT[RRCode] != null)
  781. {
  782. errmsg += (RRCode + ErrCodeHT[RRCode].ToString());
  783. errmsg += enter;
  784. errmsg += "发送EHLO命令出错,服务器可能不需要验证" + enter;
  785. }
  786. else
  787. {
  788. errmsg += RR;
  789. errmsg += "发送EHLO命令出错,不明错误,请与作者联系" + enter;
  790. }
  791. return false;
  792. }
  793. }
  794. else
  795. {
  796. System.Threading.Thread.Sleep(50);
  797. i++;
  798. if (i > 6)
  799. {
  800. errmsg += "收不到AUTH指令,可能是连接超时,或者服务器根本不需要验证" + enter;
  801. return false;
  802. }
  803. }
  804. }
  805. }
  806. else
  807. {
  808. errmsg += "发送ehlo命令失败";
  809. return false;
  810. }
  811. SendBuffer.Add("AUTH LOGIN" + enter);
  812. SendBuffer.Add(Base64Encode(username) + enter);
  813. SendBuffer.Add(Base64Encode(password) + enter);
  814. if (!Dialog(SendBuffer, "SMTP服务器验证失败,请核对用户名和密码。"))
  815. return false;
  816. return true;
  817. }
  818. #endregion
  819. #region 发送
  820. /// <summary>
  821. /// 发送邮件
  822. /// </summary>
  823. public bool SendEmail()
  824. {
  825. bool checkFlag = Check();
  826. if (!checkFlag)
  827. return false;
  828. #region 连接网络
  829. try
  830. {
  831. tc = new TcpClient(mailserver, mailserverport);
  832. ns = tc.GetStream();
  833. }
  834. catch (Exception e)
  835. {
  836. errmsg = e.ToString();
  837. return false;
  838. }
  839. //验证网络连接是否正确
  840. if (RightCodeHT[RecvResponse().Substring(0, 3)] == null)
  841. {
  842. errmsg = "网络连接失败";
  843. return false;
  844. }
  845. #endregion
  846. #region 验证发件收件人
  847. ArrayList SendBuffer = new ArrayList();
  848. string SendBufferstr;
  849. //进行SMTP验证
  850. if (useSmtpAuth)
  851. {
  852. if (!SmtpAuth())
  853. return false;
  854. }
  855. else if (usePop3Auth)
  856. {
  857. if (!Pop3Auth(tc))
  858. return false;
  859. }
  860. else
  861. {
  862. SendBufferstr = "HELO " + mailserver + enter;
  863. if (!Dialog(SendBufferstr, ""))
  864. return false;
  865. }
  866. //发件人信息
  867. SendBufferstr = "MAIL FROM:<" + From + ">" + enter;
  868. if (!Dialog(SendBufferstr, "发件人地址错误,或不能为空"))
  869. return false;
  870. //收件人列表
  871. SendBuffer.Clear();
  872. foreach (String item in Recipient)
  873. {
  874. SendBuffer.Add("RCPT TO:<" + item + ">" + enter);
  875. RecipientName = item;//这里其实只能支持一个收件人
  876. }
  877. if (!Dialog(SendBuffer, "收件人地址有误"))
  878. return false;
  879. #endregion
  880. #region 邮件头部
  881. //开始发送信件内容
  882. SendBufferstr = "DATA" + enter;
  883. if (!Dialog(SendBufferstr, ""))
  884. return false;
  885. //发件人
  886. SendBufferstr = "From:\"" + FromName + "\" <" + From + ">" + enter;
  887. //收件人
  888. SendBufferstr += "To:\"" + RecipientName + "\" <" + RecipientName + ">" + enter;
  889. //回复地址
  890. if (ReplyTo.Trim() != "")
  891. {
  892. SendBufferstr += "Reply-To: " + ReplyTo + enter;
  893. }
  894. //抄送收件人列表
  895. if (RecipientCC.Count > 0)
  896. {
  897. SendBufferstr += "CC:";
  898. foreach (String item in RecipientCC)
  899. {
  900. SendBufferstr += item + "<" + item + ">," + enter;
  901. }
  902. SendBufferstr = SendBufferstr.Substring(0, SendBufferstr.Length - 3) + enter;
  903. }
  904. //密件收件人列表
  905. if (RecipientBCC.Count > 0)
  906. {
  907. SendBufferstr += "BCC:";
  908. foreach (String item in RecipientBCC)
  909. {
  910. SendBufferstr += item + "<" + item + ">," + enter;
  911. }
  912. SendBufferstr = SendBufferstr.Substring(0, SendBufferstr.Length - 3) + enter;
  913. }
  914. //邮件主题
  915. if (Charset == "")
  916. {
  917. SendBufferstr += "Subject:" + Subject + enter;
  918. }
  919. else
  920. {
  921. SendBufferstr += "Subject:" + "=?" + Charset.ToUpper() + "?B?" + Base64Encode(Subject) + "?=" + enter;
  922. }
  923. //是否需要收件人发送收条
  924. if (true == ReturnReceipt)
  925. {
  926. SendBufferstr += "Disposition-Notification-To: \"" + FromName + "\" <" + ReplyTo + ">" + enter;
  927. }
  928. #endregion
  929. #region 邮件内容
  930. SendBufferstr += "X-Priority:" + priority + enter;
  931. SendBufferstr += "X-MSMail-Priority:" + priority + enter;
  932. SendBufferstr += "Importance:" + priority + enter;
  933. SendBufferstr += "X-Mailer: Huolx.Pubclass" + enter;
  934. SendBufferstr += "MIME-Version: 1.0" + enter;
  935. SendBufferstr += "Content-Type: multipart/mixed;" + enter;
  936. SendBufferstr += " boundary=\"" + boundary + "\"" + enter + enter;
  937. SendBufferstr += "This is a multi-part message in MIME format." + enter + enter;
  938. SendBufferstr += "--" + boundary + enter;
  939. SendBufferstr += "Content-Type: multipart/alternative;" + enter;
  940. SendBufferstr += " boundary=\"" + boundary1 + "\"" + enter + enter + enter;
  941. SendBufferstr += "--" + boundary1 + enter;
  942. //判断信件格式是否html
  943. if (IsHtml)
  944. {
  945. SendBufferstr += "Content-Type: text/html;" + enter;
  946. }
  947. else
  948. {
  949. SendBufferstr += "Content-Type: text/plain;" + enter;
  950. }
  951. //编码信息
  952. if (Charset == "")
  953. {
  954. SendBufferstr += " charset=\"iso-8859-1\"" + enter;
  955. }
  956. else
  957. {
  958. SendBufferstr += " charset=\"" + Charset.ToLower() + "\"" + enter;
  959. }
  960. SendBufferstr += "Content-Transfer-Encoding: base64" + enter;
  961. StringBuilder extras = new StringBuilder();
  962. string extrasBoundary = "--" + boundary + enter;
  963. string newBodyHtml = FixupReferences(this.Body, ref extras, extrasBoundary);
  964. SendBufferstr += enter + enter;
  965. SendBufferstr += B64StrLine(Base64Encode(newBodyHtml)) + enter;
  966. SendBufferstr += enter + "--" + boundary1 + "--" + enter + enter;
  967. SendBufferstr += extras.ToString();
  968. //如果有附件,开始发送附件.
  969. if (Attachments.Count > 0)
  970. {
  971. SendBufferstr += enter + "--" + boundary1 + "--" + enter + enter;
  972. foreach (String item in Attachments)
  973. {
  974. SendBufferstr += "--" + boundary + enter;
  975. SendBufferstr += "Content-Type: application/octet-stream;" + enter;
  976. SendBufferstr += " name=\"" + AttachmentNameStr(item.Substring(item.LastIndexOf("\\") + 1)) + "\"" + enter;
  977. SendBufferstr += "Content-Transfer-Encoding: base64" + enter;
  978. SendBufferstr += "Content-Disposition: attachment;" + enter;
  979. SendBufferstr += " filename=\"" + AttachmentNameStr(item.Substring(item.LastIndexOf("\\") + 1)) + "\"" + enter + enter;
  980. SendBufferstr += AttachmentB64Str(item) + enter + enter;
  981. }
  982. SendBufferstr += "--" + boundary + "--" + enter + enter;
  983. }
  984. SendBufferstr += enter + "." + enter;
  985. if (!Dialog(SendBufferstr, "错误信件信息"))
  986. return false;
  987. #endregion
  988. SendBufferstr = "QUIT" + enter;
  989. if (!Dialog(SendBufferstr, "断开连接时错误"))
  990. return false;
  991. ns.Close();
  992. tc.Close();
  993. return true;
  994. }
  995. private bool Pop3Auth(TcpClient tc)
  996. {
  997. try
  998. {
  999. StrmWtr = tc.GetStream();
  1000. StrmRdr = new StreamReader(tc.GetStream());
  1001. //Login
  1002. cmdData = "USER " + MailServerUsername + CRLF;
  1003. szData = System.Text.Encoding.ASCII.GetBytes(cmdData.ToCharArray());
  1004. StrmWtr.Write(szData, 0, szData.Length);
  1005. //this.getSatus();
  1006. cmdData = "PASS " + MailServerPassword + CRLF;
  1007. szData = System.Text.Encoding.ASCII.GetBytes(cmdData.ToCharArray());
  1008. StrmWtr.Write(szData, 0, szData.Length);
  1009. //Get Email's Info
  1010. cmdData = "STAT" + CRLF;
  1011. szData = System.Text.Encoding.ASCII.GetBytes(cmdData.ToCharArray());
  1012. StrmWtr.Write(szData, 0, szData.Length);
  1013. }
  1014. catch (InvalidOperationException err)
  1015. {
  1016. LogHelper.log.Error("ERROR: " + err.Message.ToString());
  1017. return false;
  1018. }
  1019. return true;
  1020. }
  1021. /// <summary>
  1022. /// 发送邮件前对参数进行检查,通过检查返回True,否则为False
  1023. /// </summary>
  1024. /// <returns>如果通过检查返回true,否则为false</returns>
  1025. private bool Check()
  1026. {
  1027. if (Recipient.Count == 0)
  1028. {
  1029. errmsg = "收件人列表不能为空";
  1030. return false;
  1031. }
  1032. if (RecipientName == "")
  1033. {
  1034. RecipientName = Recipient[0].ToString();
  1035. }
  1036. if (mailserver.Trim() == "")
  1037. {
  1038. errmsg = "必须指定SMTP服务器";
  1039. return false;
  1040. }
  1041. return true;
  1042. }
  1043. #endregion
  1044. }
  1045. }