2 // System.Web.Mail.SmtpClient.cs
5 // Per Arneng <pt99par@student.bth.se>
6 // Sanjay Gupta <gsanjay@novell.com>
7 // (C) 2004, Novell, Inc. (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
35 using System.Net.Sockets;
36 using System.Security.Permissions;
37 using System.Reflection;
39 namespace System.Web.Mail {
41 /// represents a conntection to a smtp server
42 internal class SmtpClient
45 TcpClient tcpConnection;
51 short authenticate = 1;
53 //Initialise the variables and connect
54 public SmtpClient (string server)
59 // make the actual connection
60 // and HELO handshaking
63 tcpConnection = new TcpClient (server, port);
65 NetworkStream stream = tcpConnection.GetStream ();
66 smtp = new SmtpStream (stream);
69 void ChangeToSSLSocket ()
71 // Load Mono.Security.dll
74 a = Assembly.Load (Consts.AssemblyMono_Security);
75 } catch (System.IO.FileNotFoundException) {
76 throw new SmtpException ("Cannot load Mono.Security.dll");
78 Type tSslClientStream = a.GetType ("Mono.Security.Protocol.Tls.SslClientStream");
79 object[] consArgs = new object[4];
80 consArgs[0] = smtp.Stream;
83 Type tSecurityProtocolType = a.GetType ("Mono.Security.Protocol.Tls.SecurityProtocolType");
84 int nSsl3Val = (int) Enum.Parse (tSecurityProtocolType, "Ssl3");
85 int nTlsVal = (int) Enum.Parse (tSecurityProtocolType, "Tls");
86 consArgs[3] = Enum.ToObject (tSecurityProtocolType, nSsl3Val | nTlsVal);
88 object objSslClientStream = Activator.CreateInstance (tSslClientStream, consArgs);
90 if (objSslClientStream != null)
91 smtp = new SmtpStream ((Stream)objSslClientStream);
94 void ReadFields (MailMessageWrapper msg)
97 username = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"];
98 password = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"];
99 tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"];
101 authenticate = short.Parse (tmp);
102 tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpusessl"];
104 usessl = bool.Parse (tmp);
105 tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpserverport"];
107 port = int.Parse (tmp);
110 void StartSend (MailMessageWrapper msg)
115 // read the server greeting
116 smtp.ReadResponse ();
117 smtp.CheckForStatusCode (220);
119 if (usessl || (username != null && password != null && authenticate != 1))
121 smtp.WriteEhlo (Dns.GetHostName ());
124 bool isSSL = smtp.WriteStartTLS ();
126 ChangeToSSLSocket ();
129 if (username != null && password != null && authenticate != 1) {
130 smtp.WriteAuthLogin ();
131 if (smtp.LastResponse.StatusCode == 334) {
132 smtp.WriteLine (Convert.ToBase64String (Encoding.ASCII.GetBytes (username)));
133 smtp.ReadResponse ();
134 smtp.CheckForStatusCode (334);
135 smtp.WriteLine (Convert.ToBase64String (Encoding.ASCII.GetBytes (password)));
136 smtp.ReadResponse ();
137 smtp.CheckForStatusCode (235);
141 smtp.WriteHelo (Dns.GetHostName ());
145 public void Send (MailMessageWrapper msg)
147 if (msg.From == null)
148 throw new SmtpException ("From property must be set.");
151 if (msg.To.Count < 1)
152 throw new SmtpException ("Atleast one recipient must be set.");
155 // start with a reset incase old data
156 // is present at the server in this session
159 // write the mail from command
160 smtp.WriteMailFrom (msg.From.Address);
162 // write the rcpt to command for the To addresses
163 foreach (MailAddress addr in msg.To)
164 smtp.WriteRcptTo (addr.Address);
166 // write the rcpt to command for the Cc addresses
167 foreach (MailAddress addr in msg.Cc)
168 smtp.WriteRcptTo (addr.Address);
170 // write the rcpt to command for the Bcc addresses
171 foreach (MailAddress addr in msg.Bcc)
172 smtp.WriteRcptTo (addr.Address);
174 // write the data command and then
178 if (msg.Attachments.Count == 0)
179 SendSinglepartMail (msg);
181 SendMultipartMail (msg);
183 // write the data end tag "."
184 smtp.WriteDataEndTag ();
187 // sends a single part mail to the server
188 void SendSinglepartMail (MailMessageWrapper msg)
191 smtp.WriteHeader (msg.Header);
193 // send the mail body
194 smtp.WriteBytes (msg.BodyEncoding.GetBytes (msg.Body));
197 // SECURITY-FIXME: lower assertion with imperative asserts
198 [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
199 // sends a multipart mail to the server
200 void SendMultipartMail (MailMessageWrapper msg)
202 // generate the boundary between attachments
203 string boundary = MailUtil.GenerateBoundary ();
205 // set the Content-Type header to multipart/mixed
206 string bodyContentType = msg.Header.ContentType;
208 msg.Header.ContentType = String.Concat ("multipart/mixed;\r\n boundary=", boundary);
211 smtp.WriteHeader (msg.Header);
213 // write the first part text part
214 // before the attachments
215 smtp.WriteBoundary (boundary);
217 MailHeader partHeader = new MailHeader ();
218 partHeader.ContentType = bodyContentType;
220 // Add all the custom headers to body part as specified in
221 //Fields property of MailMessageWrapper
223 //Remove fields specific for authenticating to SMTP server.
224 //Need to incorporate AUTH command in SmtpStream to handle
225 //Authorization info. Its a temporary fix for Bug no 68829.
226 //Will dig some more on SMTP AUTH command, and then implement
227 //Authorization. - Sanjay
229 if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] != null)
230 msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate");
231 if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"] != null)
232 msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendusername");
233 if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"] != null)
234 msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendpassword");
235 partHeader.Data.Add (msg.Fields.Data);
237 smtp.WriteHeader (partHeader);
239 // FIXME: probably need to use QP or Base64 on everything higher
240 // then 8-bit .. like utf-16
241 smtp.WriteBytes (msg.BodyEncoding.GetBytes (msg.Body) );
243 smtp.WriteBoundary (boundary);
245 // now start to write the attachments
247 for (int i=0; i< msg.Attachments.Count ; i++) {
248 MailAttachment a = (MailAttachment)msg.Attachments[ i ];
249 FileInfo fileInfo = new FileInfo (a.Filename);
250 MailHeader aHeader = new MailHeader ();
252 aHeader.ContentType =
253 String.Concat (MimeTypes.GetMimeType (fileInfo.Name), "; name=\"", fileInfo.Name, "\"");
255 aHeader.ContentDisposition = String.Concat ("attachment; filename=\"", fileInfo.Name, "\"");
256 aHeader.ContentTransferEncoding = a.Encoding.ToString();
257 smtp.WriteHeader (aHeader);
259 // perform the actual writing of the file.
260 // read from the file stream and write to the tcp stream
261 FileStream ins = fileInfo.OpenRead ();
263 // create an apropriate encoder
264 IAttachmentEncoder encoder;
265 if (a.Encoding == MailEncoding.UUEncode)
266 encoder = new UUAttachmentEncoder (644, fileInfo.Name );
268 encoder = new Base64AttachmentEncoder ();
270 encoder.EncodeStream (ins, smtp.Stream);
275 // if it is the last attachment write
276 // the final boundary otherwise write
278 if (i < (msg.Attachments.Count - 1))
279 smtp.WriteBoundary (boundary);
281 smtp.WriteFinalBoundary (boundary);
285 // send quit command and
286 // closes the connection
290 tcpConnection.Close();