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 ()
72 java.lang.Class c = vmw.common.TypeUtils.ToClass (smtp.Stream);
73 java.lang.reflect.Method m = c.getMethod ("ChangeToSSLSocket", null);
74 m.invoke (smtp.Stream, new object[]{});
76 // Load Mono.Security.dll
79 a = Assembly.Load (Consts.AssemblyMono_Security);
80 } catch (System.IO.FileNotFoundException) {
81 throw new SmtpException ("Cannot load Mono.Security.dll");
83 Type tSslClientStream = a.GetType ("Mono.Security.Protocol.Tls.SslClientStream");
84 object[] consArgs = new object[4];
85 consArgs[0] = smtp.Stream;
88 Type tSecurityProtocolType = a.GetType ("Mono.Security.Protocol.Tls.SecurityProtocolType");
89 int nSsl3Val = (int) Enum.Parse (tSecurityProtocolType, "Ssl3");
90 int nTlsVal = (int) Enum.Parse (tSecurityProtocolType, "Tls");
91 consArgs[3] = Enum.ToObject (tSecurityProtocolType, nSsl3Val | nTlsVal);
93 object objSslClientStream = Activator.CreateInstance (tSslClientStream, consArgs);
95 if (objSslClientStream != null)
96 smtp = new SmtpStream ((Stream)objSslClientStream);
100 void ReadFields (MailMessageWrapper msg)
103 username = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"];
104 password = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"];
105 tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"];
107 authenticate = short.Parse (tmp);
108 tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpusessl"];
110 usessl = bool.Parse (tmp);
111 tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpserverport"];
113 port = int.Parse (tmp);
116 void StartSend (MailMessageWrapper msg)
121 // read the server greeting
122 smtp.ReadResponse ();
123 smtp.CheckForStatusCode (220);
125 if (usessl || (username != null && password != null && authenticate != 1))
127 smtp.WriteEhlo (Dns.GetHostName ());
130 bool isSSL = smtp.WriteStartTLS ();
132 ChangeToSSLSocket ();
135 if (username != null && password != null && authenticate != 1) {
136 smtp.WriteAuthLogin ();
137 if (smtp.LastResponse.StatusCode == 334) {
138 smtp.WriteLine (Convert.ToBase64String (Encoding.ASCII.GetBytes (username)));
139 smtp.ReadResponse ();
140 smtp.CheckForStatusCode (334);
141 smtp.WriteLine (Convert.ToBase64String (Encoding.ASCII.GetBytes (password)));
142 smtp.ReadResponse ();
143 smtp.CheckForStatusCode (235);
147 smtp.WriteHelo (Dns.GetHostName ());
151 public void Send (MailMessageWrapper msg)
153 if (msg.From == null)
154 throw new SmtpException ("From property must be set.");
157 if (msg.To.Count < 1)
158 throw new SmtpException ("Atleast one recipient must be set.");
161 // start with a reset incase old data
162 // is present at the server in this session
165 // write the mail from command
166 smtp.WriteMailFrom (msg.From.Address);
168 // write the rcpt to command for the To addresses
169 foreach (MailAddress addr in msg.To)
170 smtp.WriteRcptTo (addr.Address);
172 // write the rcpt to command for the Cc addresses
173 foreach (MailAddress addr in msg.Cc)
174 smtp.WriteRcptTo (addr.Address);
176 // write the rcpt to command for the Bcc addresses
177 foreach (MailAddress addr in msg.Bcc)
178 smtp.WriteRcptTo (addr.Address);
180 // write the data command and then
184 if (msg.Attachments.Count == 0)
185 SendSinglepartMail (msg);
187 SendMultipartMail (msg);
189 // write the data end tag "."
190 smtp.WriteDataEndTag ();
193 // sends a single part mail to the server
194 void SendSinglepartMail (MailMessageWrapper msg)
197 smtp.WriteHeader (msg.Header);
199 // send the mail body
200 smtp.WriteBytes (msg.BodyEncoding.GetBytes (msg.Body));
203 // SECURITY-FIXME: lower assertion with imperative asserts
204 [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
205 // sends a multipart mail to the server
206 void SendMultipartMail (MailMessageWrapper msg)
208 // generate the boundary between attachments
209 string boundary = MailUtil.GenerateBoundary ();
211 // set the Content-Type header to multipart/mixed
212 string bodyContentType = msg.Header.ContentType;
214 msg.Header.ContentType = String.Concat ("multipart/mixed;\r\n boundary=", boundary);
217 smtp.WriteHeader (msg.Header);
219 // write the first part text part
220 // before the attachments
221 smtp.WriteBoundary (boundary);
223 MailHeader partHeader = new MailHeader ();
224 partHeader.ContentType = bodyContentType;
227 // Add all the custom headers to body part as specified in
228 //Fields property of MailMessageWrapper
230 //Remove fields specific for authenticating to SMTP server.
231 //Need to incorporate AUTH command in SmtpStream to handle
232 //Authorization info. Its a temporary fix for Bug no 68829.
233 //Will dig some more on SMTP AUTH command, and then implement
234 //Authorization. - Sanjay
236 if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] != null)
237 msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate");
238 if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"] != null)
239 msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendusername");
240 if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"] != null)
241 msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendpassword");
242 partHeader.Data.Add (msg.Fields.Data);
245 smtp.WriteHeader (partHeader);
247 // FIXME: probably need to use QP or Base64 on everything higher
248 // then 8-bit .. like utf-16
249 smtp.WriteBytes (msg.BodyEncoding.GetBytes (msg.Body) );
251 smtp.WriteBoundary (boundary);
253 // now start to write the attachments
255 for (int i=0; i< msg.Attachments.Count ; i++) {
256 MailAttachment a = (MailAttachment)msg.Attachments[ i ];
257 FileInfo fileInfo = new FileInfo (a.Filename);
258 MailHeader aHeader = new MailHeader ();
260 aHeader.ContentType =
261 String.Concat (MimeTypes.GetMimeType (fileInfo.Name), "; name=\"", fileInfo.Name, "\"");
263 aHeader.ContentDisposition = String.Concat ("attachment; filename=\"", fileInfo.Name, "\"");
264 aHeader.ContentTransferEncoding = a.Encoding.ToString();
265 smtp.WriteHeader (aHeader);
267 // perform the actual writing of the file.
268 // read from the file stream and write to the tcp stream
269 FileStream ins = fileInfo.OpenRead ();
271 // create an apropriate encoder
272 IAttachmentEncoder encoder;
273 if (a.Encoding == MailEncoding.UUEncode)
274 encoder = new UUAttachmentEncoder (644, fileInfo.Name );
276 encoder = new Base64AttachmentEncoder ();
278 encoder.EncodeStream (ins, smtp.Stream);
283 // if it is the last attachment write
284 // the final boundary otherwise write
286 if (i < (msg.Attachments.Count - 1))
287 smtp.WriteBoundary (boundary);
289 smtp.WriteFinalBoundary (boundary);
293 // send quit command and
294 // closes the connection
298 tcpConnection.Close();