MailSender.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Net.Mail;
  5. using System.Net.Sockets;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace BanTur.Utility
  9. {
  10. public class MailSender
  11. {
  12. private readonly string _host;
  13. private readonly int _smtpPort;
  14. private readonly string _user;
  15. private readonly string _pass;
  16. private readonly string _senderName;
  17. public MailSender(string host, int smtpPort, string user, string pass, string senderName)
  18. {
  19. _host = host;
  20. _smtpPort = smtpPort;
  21. _user = user;
  22. _pass = pass;
  23. _senderName = senderName;
  24. }
  25. public MailSender() : this(
  26. BanTurRuntime.Config.SendMailHost,
  27. BanTurRuntime.Config.SendMailPort,
  28. BanTurRuntime.Config.SendMailUser,
  29. BanTurRuntime.Config.SendMailPass,
  30. "BanTur")
  31. {
  32. }
  33. public void SendMailAndForget(string subject, string body = "")
  34. {
  35. Task.Factory.StartNew(() => SendMail(subject, body));
  36. }
  37. public void SendMail(string subject, string body = "")
  38. {
  39. SendMail(BanTurRuntime.Config.SendMailTarget, subject, BanTurRuntime.Config.SendMailTargetName, body);
  40. }
  41. public void SendMail(string to, string subject, string toName, string body)
  42. {
  43. var cl = new TcpClient();
  44. cl.SmtpConnect(_host, _smtpPort);
  45. cl.SmtpLogin(_user, _pass);
  46. cl.SmtpSendMail(_user, to, subject, body, _senderName, toName);
  47. cl.SmtpQuit();
  48. }
  49. }
  50. internal static class MailExtensions
  51. {
  52. //-- internal func --
  53. private static void SmtpCheckStatus(this TcpClient client, int status)
  54. {
  55. var line = client.ReadAsciiLine();
  56. if (line.StartsWith(status + " ") == false)
  57. throw new SmtpException(line);
  58. }
  59. //-- public func --
  60. public static void SmtpConnect(this TcpClient client, string host, int port)
  61. {
  62. client.Connect(host, port);
  63. client.SmtpCheckStatus(220);
  64. }
  65. public static void SmtpLogin(this TcpClient client, string user, string pass)
  66. {
  67. client.WriteLine("AUTH LOGIN");
  68. client.SmtpCheckStatus(334);
  69. client.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(user)));
  70. client.SmtpCheckStatus(334);
  71. client.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(pass)));
  72. client.SmtpCheckStatus(235);
  73. }
  74. public static void SmtpSendMail(this TcpClient client, string from, string to, string subject, string body = "", string senderName = "", string toName = "")
  75. {
  76. var bodyB64 = Convert.ToBase64String(
  77. Encoding.UTF8.GetBytes(body + Environment.NewLine + "Mail generate by LibSharpMail")
  78. , Base64FormattingOptions.InsertLineBreaks);
  79. client.WriteLine("MAIL FROM: <{0}>", from);
  80. client.SmtpCheckStatus(250);
  81. client.WriteLine("RCPT TO: <{0}>", to);
  82. client.SmtpCheckStatus(250);
  83. client.WriteLine("DATA");
  84. client.SmtpCheckStatus(354);
  85. client.WriteLine("Subject: {0}", subject.SubjectEncoding());
  86. client.WriteLine("From: \"{1}\" <{0}>", from, senderName.SubjectEncoding());
  87. client.WriteLine("TO: \"{1}\" <{0}>", to, toName);
  88. client.WriteLine("MIME-Version: 1.0");
  89. client.WriteLine("Content-Type: multipart/mixed; boundary=frontier");
  90. client.WriteLine("");
  91. client.WriteLine("This is a message with multiple parts in MIME format.");
  92. client.WriteLine("--frontier");
  93. client.WriteLine("Content-Type: text/plain; charset=utf-8");
  94. client.WriteLine("Content-Transfer-Encoding: base64");
  95. client.WriteLine("");
  96. client.WriteLine(bodyB64);
  97. client.WriteLine("--frontier--");
  98. client.WriteLine(".");
  99. client.SmtpCheckStatus(250);
  100. }
  101. public static void SmtpQuit(this TcpClient client)
  102. {
  103. client.WriteLine("QUIT");
  104. client.SmtpCheckStatus(221);
  105. client.Close();
  106. }
  107. ////////////////////////
  108. private const byte Cr = (byte)'\r';
  109. private const byte Lf = (byte)'\n';
  110. private static readonly byte[] CrLf = { Cr, Lf };
  111. public static string SubjectEncoding(this string subject, Encoding encoding = null)
  112. {
  113. var enc = encoding ?? Encoding.UTF8;
  114. return $"=?{enc.BodyName}?B?{Convert.ToBase64String(enc.GetBytes(subject))}?=";
  115. }
  116. public static void WriteBytes(this Stream s, byte[] bytes)
  117. {
  118. s.Write(bytes, 0, bytes.Length);
  119. }
  120. public static void WriteCrlf(this Stream s)
  121. {
  122. s.WriteBytes(CrLf);
  123. }
  124. public static void WriteLine(this TcpClient client, string s)
  125. {
  126. client.WriteLine("{0}", s);
  127. }
  128. public static void WriteLine(this TcpClient client, string fmt, params object[] args)
  129. {
  130. var s = client.GetStream();
  131. s.WriteBytes(Encoding.ASCII.GetBytes(String.Format(fmt, args)));
  132. s.WriteCrlf();
  133. }
  134. public static byte[] ReadBinLine(this TcpClient client)
  135. {
  136. var s = client.GetStream();
  137. var ms = new MemoryStream();
  138. var len = 0;
  139. do
  140. {
  141. var b = s.ReadByte();
  142. if (b < 0)
  143. continue;
  144. ms.WriteByte((byte)b);
  145. len++;
  146. if (len < 2)
  147. continue;
  148. var msbuf = ms.GetBuffer();
  149. if (msbuf[len - 2] == Cr
  150. && msbuf[len - 1] == Lf)
  151. break;
  152. } while (true);
  153. var buf = new byte[len - 2];
  154. Array.Copy(ms.GetBuffer(), 0, buf, 0, len - 2);
  155. return buf;
  156. }
  157. public static string ReadAsciiLine(this TcpClient client)
  158. {
  159. return Encoding.ASCII.GetString(client.ReadBinLine());
  160. }
  161. public static string[] ReadAsciiLinesUntilDot(this TcpClient client)
  162. {
  163. var ret = new List<string>();
  164. do
  165. {
  166. var line = client.ReadAsciiLine();
  167. if (line.Trim() == ".")
  168. break;
  169. ret.Add(line);
  170. } while (true);
  171. return ret.ToArray();
  172. }
  173. public static byte[][] ReadBinLineUntilDot(this TcpClient client)
  174. {
  175. var ret = new List<byte[]>();
  176. do
  177. {
  178. var line = client.ReadBinLine();
  179. if (line.Length > 0 && line[0] == (byte)'.')
  180. break;
  181. ret.Add(line);
  182. } while (true);
  183. return ret.ToArray();
  184. }
  185. }
  186. }