actually I gave wrong file name.
[mono.git] / mcs / class / System.Web / System.Web.Mail / SmtpClient.cs
1 //
2 // System.Web.Mail.SmtpClient.cs
3 //
4 // Author(s):
5 //   Per Arneng <pt99par@student.bth.se>
6 //   Sanjay Gupta <gsanjay@novell.com>
7 //   (C) 2004, Novell, Inc. (http://www.novell.com)
8 //
9
10 //
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:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
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.
29 //
30
31 using System.Net;
32 using System.IO;
33 using System.Text;
34 using System.Collections;
35 using System.Net.Sockets;
36 using System.Security.Permissions;
37 using System.Reflection;
38
39 namespace System.Web.Mail {
40
41     /// represents a conntection to a smtp server
42     internal class SmtpClient {
43         
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;         
52         
53     //Initialise the variables and connect
54         public SmtpClient( string server ) {
55             
56             this.server = server;
57             Connect();
58         }
59         
60         // make the actual connection
61         // and HELO handshaking
62         private void Connect() {
63             tcpConnection = new TcpClient( server , port );
64             
65             NetworkStream stream = tcpConnection.GetStream();
66             smtp = new SmtpStream( stream );
67         }
68                     
69         private void ChangeToSSLSocket( ) {
70 #if TARGET_JVM
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[]{});
74 #else
75                 // Load Mono.Security.dll
76                 Assembly a;
77                 try {
78                         a = Assembly.Load(Consts.AssemblyMono_Security);
79                 }
80                 catch(System.IO.FileNotFoundException) {
81                         throw new SmtpException( "Cannot load Mono.Security.dll" );
82                 }
83                 Type tSslClientStream = a.GetType("Mono.Security.Protocol.Tls.SslClientStream");
84                 object[] consArgs = new object[4];
85                 consArgs[0] = smtp.Stream;
86                 consArgs[1] = server;
87                 consArgs[2] = true;
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);
92
93                 object objSslClientStream = 
94                         Activator.CreateInstance(tSslClientStream, consArgs); 
95
96                 if (objSslClientStream != null)
97                         smtp = new SmtpStream( (Stream)objSslClientStream );
98 #endif
99         }
100                 
101         private void ReadFields(MailMessageWrapper msg)
102         {
103                 string tmp;
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"]; 
107                 if (tmp != null)
108                         authenticate = short.Parse(tmp);
109                 tmp = msg.Fields.Data["http://schemas.microsoft.com/cdo/configuration/smtpusessl"];     
110                 if (tmp != null)
111                         usessl = bool.Parse(tmp);
112                 tmp = msg.Fields.Data["http://schemas.microsoft.com/cdo/configuration/smtpserverport"]; 
113                 if (tmp != null)
114                         port = int.Parse(tmp);
115         }
116
117         private void StartSend(MailMessageWrapper msg)
118         {
119                 ReadFields(msg);
120                 
121                 // read the server greeting
122                 smtp.ReadResponse();
123                 smtp.CheckForStatusCode( 220 );
124
125                 if (usessl || (username != null && password != null && authenticate != 1)) 
126                 {
127                         smtp.WriteEhlo( Dns.GetHostName() );
128
129                         if (usessl) {
130                                 bool isSSL = smtp.WriteStartTLS();
131                                 if (isSSL)
132                                         ChangeToSSLSocket();
133                         }
134
135                         if (username != null && password != null && authenticate != 1) 
136                         {
137                                 smtp.WriteAuthLogin();
138                                 if (smtp.LastResponse.StatusCode == 334) 
139                                 {
140                                         smtp.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(username)));
141                                         smtp.ReadResponse();
142                                         smtp.CheckForStatusCode(334);
143                                         smtp.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(password)));
144                                         smtp.ReadResponse();
145                                         smtp.CheckForStatusCode(235);
146                                 }
147                         }
148                 }
149                 else 
150                 {
151                         smtp.WriteHelo( Dns.GetHostName() );
152                 }
153         }
154         
155         public void Send( MailMessageWrapper msg ) {
156             
157             if( msg.From == null ) {
158                 throw new SmtpException( "From property must be set." );
159             }
160
161             if( msg.To == null ) {
162                 if( msg.To.Count < 1 ) throw new SmtpException( "Atleast one recipient must be set." );
163             }
164             
165             StartSend (msg);
166             // start with a reset incase old data
167             // is present at the server in this session
168             smtp.WriteRset();
169             
170             // write the mail from command
171             smtp.WriteMailFrom( msg.From.Address );
172             
173             // write the rcpt to command for the To addresses
174             foreach( MailAddress addr in msg.To ) {
175                 smtp.WriteRcptTo( addr.Address );
176             }
177
178             // write the rcpt to command for the Cc addresses
179             foreach( MailAddress addr in msg.Cc ) {
180                 smtp.WriteRcptTo( addr.Address );
181             }
182
183             // write the rcpt to command for the Bcc addresses
184             foreach( MailAddress addr in msg.Bcc ) {
185                 smtp.WriteRcptTo( addr.Address );
186             }
187             
188             // write the data command and then
189             // send the email
190             smtp.WriteData();
191                 
192             if( msg.Attachments.Count == 0 ) {
193                 SendSinglepartMail( msg );          
194             } else {
195                 
196                 SendMultipartMail( msg );
197             
198             }
199
200             // write the data end tag "."
201             smtp.WriteDataEndTag();
202
203         }
204         
205         // sends a single part mail to the server
206         private void SendSinglepartMail( MailMessageWrapper msg ) {
207                             
208             // write the header
209             smtp.WriteHeader( msg.Header );
210             
211             // send the mail body
212             smtp.WriteBytes( msg.BodyEncoding.GetBytes( msg.Body ) );
213
214         }
215
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 ) {
220                     
221             // generate the boundary between attachments
222             string boundary = MailUtil.GenerateBoundary();
223                 
224             // set the Content-Type header to multipart/mixed
225             string bodyContentType = msg.Header.ContentType;
226
227             msg.Header.ContentType = 
228                 String.Format( "multipart/mixed;\r\n   boundary={0}" , boundary );
229                 
230             // write the header
231             smtp.WriteHeader( msg.Header );
232                 
233             // write the first part text part
234             // before the attachments
235             smtp.WriteBoundary( boundary );
236                 
237             MailHeader partHeader = new MailHeader();
238             partHeader.ContentType = bodyContentType;           
239
240 #if NET_1_1
241                 // Add all the custom headers to body part as specified in 
242                 //Fields property of MailMessageWrapper
243
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
249
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);
257 #endif
258
259             smtp.WriteHeader( partHeader );
260           
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 )  );
264
265             smtp.WriteBoundary( boundary );
266
267             // now start to write the attachments
268             
269             for( int i=0; i< msg.Attachments.Count ; i++ ) {
270                 MailAttachment a = (MailAttachment)msg.Attachments[ i ];
271                         
272                 FileInfo fileInfo = new FileInfo( a.Filename );
273
274                 MailHeader aHeader = new MailHeader();
275                 
276                 aHeader.ContentType = 
277                     String.Format (MimeTypes.GetMimeType (fileInfo.Name) + "; name=\"{0}\"",fileInfo.Name);
278                 
279                 aHeader.ContentDisposition = 
280                     String.Format( "attachment; filename=\"{0}\"" , fileInfo.Name );
281                 
282                 aHeader.ContentTransferEncoding = a.Encoding.ToString();
283                                 
284                 smtp.WriteHeader( aHeader );
285                    
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 ();
289                 
290                 // create an apropriate encoder
291                 IAttachmentEncoder encoder;
292                 if( a.Encoding == MailEncoding.UUEncode ) {
293                     encoder = new UUAttachmentEncoder( 644 , fileInfo.Name  );
294                 } else {
295                     encoder = new Base64AttachmentEncoder();
296                 }
297                 
298                 encoder.EncodeStream( ins , smtp.Stream );
299                 
300                 ins.Close();
301                 
302                     
303                 smtp.WriteLine( "" );
304                 
305                 // if it is the last attachment write
306                 // the final boundary otherwise write
307                 // a normal one.
308                 if( i < (msg.Attachments.Count - 1) ) { 
309                     smtp.WriteBoundary( boundary );
310                 } else {
311                     smtp.WriteFinalBoundary( boundary );
312                 }
313                     
314             }
315                
316         }
317         
318         // send quit command and
319         // closes the connection
320         public void Close() {
321             
322             smtp.WriteQuit();
323             tcpConnection.Close();
324         
325         }
326         
327                 
328     }
329 }