SmtpClient.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. //
  2. // System.Web.Mail.SmtpClient.cs
  3. //
  4. // Author(s):
  5. // Per Arneng <[email protected]>
  6. // Sanjay Gupta <[email protected]>
  7. // (C) 2004, Novell, Inc. (http://www.novell.com)
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System.Net;
  30. using System.IO;
  31. using System.Text;
  32. using System.Collections;
  33. using System.Net.Sockets;
  34. using System.Security.Permissions;
  35. using System.Reflection;
  36. namespace System.Web.Mail {
  37. /// represents a conntection to a smtp server
  38. internal class SmtpClient
  39. {
  40. string server;
  41. TcpClient tcpConnection;
  42. SmtpStream smtp;
  43. string username;
  44. string password;
  45. int port = 25;
  46. bool usessl = false;
  47. short authenticate = 1;
  48. //Initialise the variables and connect
  49. public SmtpClient (string server)
  50. {
  51. this.server = server;
  52. }
  53. // make the actual connection
  54. // and HELO handshaking
  55. void Connect ()
  56. {
  57. tcpConnection = new TcpClient (server, port);
  58. NetworkStream stream = tcpConnection.GetStream ();
  59. smtp = new SmtpStream (stream);
  60. }
  61. void ChangeToSSLSocket ()
  62. {
  63. #if TARGET_JVM
  64. java.lang.Class c = vmw.common.TypeUtils.ToClass (smtp.Stream);
  65. java.lang.reflect.Method m = c.getMethod ("ChangeToSSLSocket", null);
  66. m.invoke (smtp.Stream, new object[]{});
  67. #else
  68. // Load Mono.Security.dll
  69. Assembly a;
  70. try {
  71. a = Assembly.Load (Consts.AssemblyMono_Security);
  72. } catch (System.IO.FileNotFoundException) {
  73. throw new SmtpException ("Cannot load Mono.Security.dll");
  74. }
  75. Type tSslClientStream = a.GetType ("Mono.Security.Protocol.Tls.SslClientStream");
  76. object[] consArgs = new object[4];
  77. consArgs[0] = smtp.Stream;
  78. consArgs[1] = server;
  79. consArgs[2] = true;
  80. Type tSecurityProtocolType = a.GetType ("Mono.Security.Protocol.Tls.SecurityProtocolType");
  81. int nSsl3Val = (int) Enum.Parse (tSecurityProtocolType, "Ssl3");
  82. int nTlsVal = (int) Enum.Parse (tSecurityProtocolType, "Tls");
  83. consArgs[3] = Enum.ToObject (tSecurityProtocolType, nSsl3Val | nTlsVal);
  84. object objSslClientStream = Activator.CreateInstance (tSslClientStream, consArgs);
  85. if (objSslClientStream != null)
  86. smtp = new SmtpStream ((Stream)objSslClientStream);
  87. #endif
  88. }
  89. void ReadFields (MailMessageWrapper msg)
  90. {
  91. string tmp;
  92. username = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"];
  93. password = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"];
  94. tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"];
  95. if (tmp != null)
  96. authenticate = short.Parse (tmp);
  97. tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpusessl"];
  98. if (tmp != null)
  99. usessl = bool.Parse (tmp);
  100. tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpserverport"];
  101. if (tmp != null)
  102. port = int.Parse (tmp);
  103. }
  104. void StartSend (MailMessageWrapper msg)
  105. {
  106. ReadFields (msg);
  107. Connect ();
  108. // read the server greeting
  109. smtp.ReadResponse ();
  110. smtp.CheckForStatusCode (220);
  111. if (usessl || (username != null && password != null && authenticate != 1))
  112. {
  113. smtp.WriteEhlo (Dns.GetHostName ());
  114. if (usessl) {
  115. bool isSSL = smtp.WriteStartTLS ();
  116. if (isSSL)
  117. ChangeToSSLSocket ();
  118. }
  119. if (username != null && password != null && authenticate != 1) {
  120. smtp.WriteAuthLogin ();
  121. if (smtp.LastResponse.StatusCode == 334) {
  122. smtp.WriteLine (Convert.ToBase64String (Encoding.ASCII.GetBytes (username)));
  123. smtp.ReadResponse ();
  124. smtp.CheckForStatusCode (334);
  125. smtp.WriteLine (Convert.ToBase64String (Encoding.ASCII.GetBytes (password)));
  126. smtp.ReadResponse ();
  127. smtp.CheckForStatusCode (235);
  128. }
  129. }
  130. } else {
  131. smtp.WriteHelo (Dns.GetHostName ());
  132. }
  133. }
  134. public void Send (MailMessageWrapper msg)
  135. {
  136. if (msg.From == null)
  137. throw new SmtpException ("From property must be set.");
  138. if (msg.To == null)
  139. if (msg.To.Count < 1)
  140. throw new SmtpException ("Atleast one recipient must be set.");
  141. StartSend (msg);
  142. // start with a reset incase old data
  143. // is present at the server in this session
  144. smtp.WriteRset ();
  145. // write the mail from command
  146. smtp.WriteMailFrom (msg.From.Address);
  147. // write the rcpt to command for the To addresses
  148. foreach (MailAddress addr in msg.To)
  149. smtp.WriteRcptTo (addr.Address);
  150. // write the rcpt to command for the Cc addresses
  151. foreach (MailAddress addr in msg.Cc)
  152. smtp.WriteRcptTo (addr.Address);
  153. // write the rcpt to command for the Bcc addresses
  154. foreach (MailAddress addr in msg.Bcc)
  155. smtp.WriteRcptTo (addr.Address);
  156. // write the data command and then
  157. // send the email
  158. smtp.WriteData ();
  159. if (msg.Attachments.Count == 0)
  160. SendSinglepartMail (msg);
  161. else
  162. SendMultipartMail (msg);
  163. // write the data end tag "."
  164. smtp.WriteDataEndTag ();
  165. }
  166. // sends a single part mail to the server
  167. void SendSinglepartMail (MailMessageWrapper msg)
  168. {
  169. // write the header
  170. smtp.WriteHeader (msg.Header);
  171. // send the mail body
  172. smtp.WriteBytes (msg.BodyEncoding.GetBytes (msg.Body));
  173. }
  174. // SECURITY-FIXME: lower assertion with imperative asserts
  175. [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
  176. // sends a multipart mail to the server
  177. void SendMultipartMail (MailMessageWrapper msg)
  178. {
  179. // generate the boundary between attachments
  180. string boundary = MailUtil.GenerateBoundary ();
  181. // set the Content-Type header to multipart/mixed
  182. string bodyContentType = msg.Header.ContentType;
  183. msg.Header.ContentType = String.Concat ("multipart/mixed;\r\n boundary=", boundary);
  184. // write the header
  185. smtp.WriteHeader (msg.Header);
  186. // write the first part text part
  187. // before the attachments
  188. smtp.WriteBoundary (boundary);
  189. MailHeader partHeader = new MailHeader ();
  190. partHeader.ContentType = bodyContentType;
  191. #if NET_1_1
  192. // Add all the custom headers to body part as specified in
  193. //Fields property of MailMessageWrapper
  194. //Remove fields specific for authenticating to SMTP server.
  195. //Need to incorporate AUTH command in SmtpStream to handle
  196. //Authorization info. Its a temporary fix for Bug no 68829.
  197. //Will dig some more on SMTP AUTH command, and then implement
  198. //Authorization. - Sanjay
  199. if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] != null)
  200. msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate");
  201. if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"] != null)
  202. msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendusername");
  203. if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"] != null)
  204. msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendpassword");
  205. partHeader.Data.Add (msg.Fields.Data);
  206. #endif
  207. smtp.WriteHeader (partHeader);
  208. // FIXME: probably need to use QP or Base64 on everything higher
  209. // then 8-bit .. like utf-16
  210. smtp.WriteBytes (msg.BodyEncoding.GetBytes (msg.Body) );
  211. smtp.WriteBoundary (boundary);
  212. // now start to write the attachments
  213. for (int i=0; i< msg.Attachments.Count ; i++) {
  214. MailAttachment a = (MailAttachment)msg.Attachments[ i ];
  215. FileInfo fileInfo = new FileInfo (a.Filename);
  216. MailHeader aHeader = new MailHeader ();
  217. aHeader.ContentType =
  218. String.Concat (MimeTypes.GetMimeType (fileInfo.Name), "; name=\"", fileInfo.Name, "\"");
  219. aHeader.ContentDisposition = String.Concat ("attachment; filename=\"", fileInfo.Name, "\"");
  220. aHeader.ContentTransferEncoding = a.Encoding.ToString();
  221. smtp.WriteHeader (aHeader);
  222. // perform the actual writing of the file.
  223. // read from the file stream and write to the tcp stream
  224. FileStream ins = fileInfo.OpenRead ();
  225. // create an apropriate encoder
  226. IAttachmentEncoder encoder;
  227. if (a.Encoding == MailEncoding.UUEncode)
  228. encoder = new UUAttachmentEncoder (644, fileInfo.Name );
  229. else
  230. encoder = new Base64AttachmentEncoder ();
  231. encoder.EncodeStream (ins, smtp.Stream);
  232. ins.Close ();
  233. smtp.WriteLine ("");
  234. // if it is the last attachment write
  235. // the final boundary otherwise write
  236. // a normal one.
  237. if (i < (msg.Attachments.Count - 1))
  238. smtp.WriteBoundary (boundary);
  239. else
  240. smtp.WriteFinalBoundary (boundary);
  241. }
  242. }
  243. // send quit command and
  244. // closes the connection
  245. public void Close()
  246. {
  247. smtp.WriteQuit();
  248. tcpConnection.Close();
  249. }
  250. }
  251. }