SmtpClient.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. //
  2. // System.Web.Mail.SmtpClient.cs
  3. //
  4. // Author(s):
  5. // Per Arneng <[email protected]>
  6. //
  7. //
  8. using System;
  9. using System.Net;
  10. using System.IO;
  11. using System.Text;
  12. using System.Collections;
  13. using System.Net.Sockets;
  14. namespace System.Web.Mail {
  15. /// represents a conntection to a smtp server
  16. internal class SmtpClient {
  17. private string server;
  18. private TcpClient tcpConnection;
  19. private SmtpStream smtp;
  20. private Encoding encoding;
  21. //Initialise the variables and connect
  22. public SmtpClient( string server ) {
  23. this.server = server;
  24. encoding = new ASCIIEncoding( );
  25. Connect();
  26. }
  27. // make the actual connection
  28. // and HELO handshaking
  29. private void Connect() {
  30. tcpConnection = new TcpClient( server , 25 );
  31. Stream stream = tcpConnection.GetStream();
  32. smtp = new SmtpStream( stream );
  33. // read the server greeting
  34. smtp.ReadResponse();
  35. smtp.CheckForStatusCode( 220 );
  36. // write the HELO command to the server
  37. smtp.WriteHelo( Dns.GetHostName() );
  38. }
  39. public void Send( MailMessageWrapper msg ) {
  40. if( msg.From == null ) {
  41. throw new SmtpException( "From property must be set." );
  42. }
  43. if( msg.To == null ) {
  44. if( msg.To.Count < 1 ) throw new SmtpException( "Atleast one recipient must be set." );
  45. }
  46. // if no encoding is set then set the system
  47. // default encoding
  48. if( msg.BodyEncoding == null )
  49. msg.BodyEncoding = Encoding.Default;
  50. // start with a reset incase old data
  51. // is present at the server in this session
  52. smtp.WriteRset();
  53. // write the mail from command
  54. smtp.WriteMailFrom( msg.From.Address );
  55. // write the rcpt to command for the To addresses
  56. foreach( MailAddress addr in msg.To ) {
  57. smtp.WriteRcptTo( addr.Address );
  58. }
  59. // write the rcpt to command for the Cc addresses
  60. foreach( MailAddress addr in msg.Cc ) {
  61. smtp.WriteRcptTo( addr.Address );
  62. }
  63. // write the data command and then
  64. // send the email
  65. smtp.WriteData();
  66. if( msg.Attachments.Count == 0 ) {
  67. SendSinglepartMail( msg );
  68. } else {
  69. SendMultipartMail( msg );
  70. }
  71. // write the data end tag "."
  72. smtp.WriteDataEndTag();
  73. }
  74. // sends a single part mail to the server
  75. private void SendSinglepartMail( MailMessageWrapper msg ) {
  76. // create the headers
  77. IDictionary headers = CreateHeaders( msg );
  78. smtp.WriteHeaders( headers );
  79. // send the mail body FIXME
  80. smtp.WriteBytes( msg.BodyEncoding.GetBytes( msg.Body ) );
  81. }
  82. // sends a multipart mail to the server
  83. private void SendMultipartMail( MailMessageWrapper msg ) {
  84. // create the headers
  85. IDictionary headers = CreateHeaders( msg );
  86. // set the part boundary FIXME: THIS SHOULD NOT BE HARDCODED
  87. // look att Gaurav Vaish implementation
  88. string boundary = "NextPart_000_1113_1962_1fe8";
  89. // set the Content-Type header to multipart/mixed
  90. headers[ "Content-Type" ] =
  91. String.Format( "multipart/mixed;\r\n boundary={0}" , boundary );
  92. // write the headers
  93. // and start writing the multipart body
  94. smtp.WriteHeaders( headers );
  95. // write the first part text part
  96. // before the attachments
  97. smtp.WriteBoundary( boundary );
  98. Hashtable partHeaders = new Hashtable();
  99. partHeaders[ "Content-Type" ] = "text/plain";
  100. smtp.WriteHeaders( partHeaders );
  101. // FIXME: probably need to use QP or Base64 on everything higher
  102. // then 8-bit .. like utf-16
  103. smtp.WriteBytes( msg.BodyEncoding.GetBytes( msg.Body ) );
  104. smtp.WriteBoundary( boundary );
  105. // now start to write the attachments
  106. for( int i=0; i< msg.Attachments.Count ; i++ ) {
  107. MailAttachment a = (MailAttachment)msg.Attachments[ i ];
  108. FileInfo fileInfo = new FileInfo( a.Filename );
  109. Hashtable aHeaders = new Hashtable();
  110. aHeaders[ "Content-Type" ] =
  111. String.Format( "application/octet-stream; name=\"{0}\"",
  112. fileInfo.Name );
  113. aHeaders[ "Content-Disposition" ] =
  114. String.Format( "attachment; filename=\"{0}\"" , fileInfo.Name );
  115. aHeaders[ "Content-Transfer-Encoding" ] =
  116. (a.Encoding == MailEncoding.UUEncode ? "UUEncode" : "Base64" );
  117. smtp.WriteHeaders( aHeaders );
  118. // perform the actual writing of the file.
  119. // read from the file stream and write to the tcp stream
  120. FileStream ins = new FileStream( fileInfo.FullName , FileMode.Open );
  121. // create an apropriate encoder
  122. IAttachmentEncoder encoder;
  123. if( a.Encoding == MailEncoding.UUEncode ) {
  124. encoder = new UUAttachmentEncoder( 644 , fileInfo.Name );
  125. } else {
  126. encoder = new Base64AttachmentEncoder();
  127. }
  128. encoder.EncodeStream( ins , smtp.Stream );
  129. ins.Close();
  130. smtp.WriteLine( "" );
  131. // if it is the last attachment write
  132. // the final boundary otherwise write
  133. // a normal one.
  134. if( i < (msg.Attachments.Count - 1) ) {
  135. smtp.WriteBoundary( boundary );
  136. } else {
  137. smtp.WriteFinalBoundary( boundary );
  138. }
  139. }
  140. }
  141. // send the standard headers
  142. // and the custom in MailMessage
  143. private IDictionary CreateHeaders( MailMessageWrapper msg ) {
  144. Hashtable headers = new Hashtable();
  145. headers[ "From" ] = msg.From.ToString();
  146. headers[ "To" ] = msg.To.ToString();
  147. if( msg.Cc.Count > 0 ) headers[ "Cc" ] = msg.Cc.ToString();
  148. if( msg.Bcc.Count > 0 ) headers[ "Bcc" ] = msg.Bcc.ToString();
  149. if( HasData( msg.Subject ) ) {
  150. // if the BodyEncoding is not 7bit us-ascii then
  151. // convert using base64
  152. if( msg.BodyEncoding is ASCIIEncoding ) {
  153. headers[ "Subject" ] = msg.Subject;
  154. } else {
  155. byte[] subjectBytes = msg.BodyEncoding.GetBytes( msg.Subject );
  156. // encode the subject with Base64
  157. headers[ "Subject" ] =
  158. String.Format( "=?{0}?{1}?{2}?=" ,
  159. msg.BodyEncoding.BodyName , "B",
  160. Convert.ToBase64String( subjectBytes ) );
  161. }
  162. }
  163. if( HasData( msg.UrlContentBase ) )
  164. headers[ "Content-Base" ] = msg.UrlContentBase;
  165. if( HasData( msg.UrlContentLocation ) )
  166. headers[ "Content-Location" ] = msg.UrlContentLocation;
  167. string charset = String.Format( "charset=\"{0}\"" , msg.BodyEncoding.BodyName );
  168. // set body the content type
  169. switch( msg.BodyFormat ) {
  170. case MailFormat.Html:
  171. headers[ "Content-Type" ] = "text/html; " + charset;
  172. break;
  173. case MailFormat.Text:
  174. headers[ "Content-Type" ] = "text/plain; " + charset;
  175. break;
  176. default:
  177. headers[ "Content-Type" ] = "text/plain; " + charset;
  178. break;
  179. }
  180. // set the priority as in the same way as .NET sdk does
  181. switch( msg.Priority ) {
  182. case MailPriority.High:
  183. headers[ "Importance" ] = "high";
  184. break;
  185. case MailPriority.Low:
  186. headers[ "Importance" ] = "low";
  187. break;
  188. case MailPriority.Normal:
  189. headers[ "Importance" ] = "normal";
  190. break;
  191. default:
  192. headers[ "Importance" ] = "normal";
  193. break;
  194. }
  195. // .NET sdk allways sets this to normal
  196. headers[ "Priority" ] = "normal";
  197. // add mime version
  198. headers[ "Mime-Version" ] = "1.0";
  199. // set the mailer -- should probably be changed
  200. headers[ "X-Mailer" ] = "Mono (System.Web.Mail.SmtpMail.Send)";
  201. // Set the transfer encoding.. it seems like only sends 7bit
  202. // if it is ASCII
  203. if( msg.BodyEncoding is ASCIIEncoding ) {
  204. headers[ "Content-Transfer-Encoding" ] = "7bit";
  205. } else {
  206. headers[ "Content-Transfer-Encoding" ] = "8bit";
  207. }
  208. // add the custom headers they will overwrite
  209. // the earlier ones if they are the same
  210. foreach( string key in msg.Headers.Keys )
  211. headers[ key ] = (string)msg.Headers[ key ];
  212. return headers;
  213. }
  214. // returns true if str is not null and not
  215. // empty
  216. private bool HasData( string str ) {
  217. bool hasData = false;
  218. if( str != null ) {
  219. if( str.Length > 0 ) {
  220. hasData = true;
  221. }
  222. }
  223. return hasData;
  224. }
  225. // send quit command and
  226. // closes the connection
  227. public void Close() {
  228. smtp.WriteQuit();
  229. tcpConnection.Close();
  230. }
  231. }
  232. }