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 {
44 private string server;
45 private TcpClient tcpConnection;
46 private SmtpStream smtp;
47 private string username;
48 private string password;
49 private int port = 25;
50 private bool usessl = false;
51 private short authenticate = 1;
53 //Initialise the variables and connect
54 public SmtpClient( string server ) {
60 // make the actual connection
61 // and HELO handshaking
62 private void Connect() {
63 tcpConnection = new TcpClient( server , port );
65 NetworkStream stream = tcpConnection.GetStream();
66 smtp = new SmtpStream( stream );
69 private void ChangeToSSLSocket( ) {
71 java.lang.Class c = vmw.common.TypeUtils.ToClass( smtp.Stream );
72 java.lang.reflect.Method m = c.getMethod("ChangeToSSLSocket", null);
73 m.invoke(smtp.Stream, new object[]{});
75 // Load Mono.Security.dll
78 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 =
94 Activator.CreateInstance(tSslClientStream, consArgs);
96 if (objSslClientStream != null)
97 smtp = new SmtpStream( (Stream)objSslClientStream );
101 private void ReadFields(MailMessageWrapper msg)
104 username = msg.Fields.Data["http://schemas.microsoft.com/cdo/configuration/sendusername"];
105 password = msg.Fields.Data["http://schemas.microsoft.com/cdo/configuration/sendpassword"];
106 tmp = msg.Fields.Data["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"];
108 authenticate = short.Parse(tmp);
109 tmp = msg.Fields.Data["http://schemas.microsoft.com/cdo/configuration/smtpusessl"];
111 usessl = bool.Parse(tmp);
112 tmp = msg.Fields.Data["http://schemas.microsoft.com/cdo/configuration/smtpserverport"];
114 port = int.Parse(tmp);
117 private void StartSend(MailMessageWrapper msg)
121 // read the server greeting
123 smtp.CheckForStatusCode( 220 );
125 if (usessl || (username != null && password != null && authenticate != 1))
127 smtp.WriteEhlo( Dns.GetHostName() );
130 bool isSSL = smtp.WriteStartTLS();
135 if (username != null && password != null && authenticate != 1)
137 smtp.WriteAuthLogin();
138 if (smtp.LastResponse.StatusCode == 334)
140 smtp.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(username)));
142 smtp.CheckForStatusCode(334);
143 smtp.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(password)));
145 smtp.CheckForStatusCode(235);
151 smtp.WriteHelo( Dns.GetHostName() );
155 public void Send( MailMessageWrapper msg ) {
157 if( msg.From == null ) {
158 throw new SmtpException( "From property must be set." );
161 if( msg.To == null ) {
162 if( msg.To.Count < 1 ) throw new SmtpException( "Atleast one recipient must be set." );
166 // start with a reset incase old data
167 // is present at the server in this session
170 // write the mail from command
171 smtp.WriteMailFrom( msg.From.Address );
173 // write the rcpt to command for the To addresses
174 foreach( MailAddress addr in msg.To ) {
175 smtp.WriteRcptTo( addr.Address );
178 // write the rcpt to command for the Cc addresses
179 foreach( MailAddress addr in msg.Cc ) {
180 smtp.WriteRcptTo( addr.Address );
183 // write the rcpt to command for the Bcc addresses
184 foreach( MailAddress addr in msg.Bcc ) {
185 smtp.WriteRcptTo( addr.Address );
188 // write the data command and then
192 if( msg.Attachments.Count == 0 ) {
193 SendSinglepartMail( msg );
196 SendMultipartMail( msg );
200 // write the data end tag "."
201 smtp.WriteDataEndTag();
205 // sends a single part mail to the server
206 private void SendSinglepartMail( MailMessageWrapper msg ) {
209 smtp.WriteHeader( msg.Header );
211 // send the mail body
212 smtp.WriteBytes( msg.BodyEncoding.GetBytes( msg.Body ) );
216 // SECURITY-FIXME: lower assertion with imperative asserts
217 [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
218 // sends a multipart mail to the server
219 private void SendMultipartMail( MailMessageWrapper msg ) {
221 // generate the boundary between attachments
222 string boundary = MailUtil.GenerateBoundary();
224 // set the Content-Type header to multipart/mixed
225 string bodyContentType = msg.Header.ContentType;
227 msg.Header.ContentType =
228 String.Format( "multipart/mixed;\r\n boundary={0}" , boundary );
231 smtp.WriteHeader( msg.Header );
233 // write the first part text part
234 // before the attachments
235 smtp.WriteBoundary( boundary );
237 MailHeader partHeader = new MailHeader();
238 partHeader.ContentType = bodyContentType;
241 // Add all the custom headers to body part as specified in
242 //Fields property of MailMessageWrapper
244 //Remove fields specific for authenticating to SMTP server.
245 //Need to incorporate AUTH command in SmtpStream to handle
246 //Authorization info. Its a temporary fix for Bug no 68829.
247 //Will dig some more on SMTP AUTH command, and then implement
248 //Authorization. - Sanjay
250 if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] != null)
251 msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate");
252 if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"] != null)
253 msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendusername");
254 if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"] != null)
255 msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendpassword");
256 partHeader.Data.Add (msg.Fields.Data);
259 smtp.WriteHeader( partHeader );
261 // FIXME: probably need to use QP or Base64 on everything higher
262 // then 8-bit .. like utf-16
263 smtp.WriteBytes( msg.BodyEncoding.GetBytes( msg.Body ) );
265 smtp.WriteBoundary( boundary );
267 // now start to write the attachments
269 for( int i=0; i< msg.Attachments.Count ; i++ ) {
270 MailAttachment a = (MailAttachment)msg.Attachments[ i ];
272 FileInfo fileInfo = new FileInfo( a.Filename );
274 MailHeader aHeader = new MailHeader();
276 aHeader.ContentType =
277 String.Format (MimeTypes.GetMimeType (fileInfo.Name) + "; name=\"{0}\"",fileInfo.Name);
279 aHeader.ContentDisposition =
280 String.Format( "attachment; filename=\"{0}\"" , fileInfo.Name );
282 aHeader.ContentTransferEncoding = a.Encoding.ToString();
284 smtp.WriteHeader( aHeader );
286 // perform the actual writing of the file.
287 // read from the file stream and write to the tcp stream
288 FileStream ins = fileInfo.OpenRead ();
290 // create an apropriate encoder
291 IAttachmentEncoder encoder;
292 if( a.Encoding == MailEncoding.UUEncode ) {
293 encoder = new UUAttachmentEncoder( 644 , fileInfo.Name );
295 encoder = new Base64AttachmentEncoder();
298 encoder.EncodeStream( ins , smtp.Stream );
303 smtp.WriteLine( "" );
305 // if it is the last attachment write
306 // the final boundary otherwise write
308 if( i < (msg.Attachments.Count - 1) ) {
309 smtp.WriteBoundary( boundary );
311 smtp.WriteFinalBoundary( boundary );
318 // send quit command and
319 // closes the connection
320 public void Close() {
323 tcpConnection.Close();