* Mono.Posix_test.dll.sources: Move StdlibTest into the Mono.Unix.Native
[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
38 namespace System.Web.Mail {
39 #if NET_2_0
40 #pragma warning disable 618
41 #endif
42
43     /// represents a conntection to a smtp server
44     internal class SmtpClient {
45         
46         private string server;
47         private TcpClient tcpConnection;
48         private SmtpStream smtp;
49         
50         //Initialise the variables and connect
51         public SmtpClient( string server ) {
52             
53             this.server = server;
54             Connect();
55         }
56         
57         // make the actual connection
58         // and HELO handshaking
59         private void Connect() {
60             tcpConnection = new TcpClient( server , 25 );
61             
62             Stream stream = tcpConnection.GetStream();
63             smtp = new SmtpStream( stream );
64             
65             // read the server greeting
66             smtp.ReadResponse();
67             smtp.CheckForStatusCode( 220 );
68            
69             // write the HELO command to the server
70             smtp.WriteHelo( Dns.GetHostName() );
71                     
72         }
73         
74         public void Send( MailMessageWrapper msg ) {
75             
76             if( msg.From == null ) {
77                 throw new SmtpException( "From property must be set." );
78             }
79
80             if( msg.To == null ) {
81                 if( msg.To.Count < 1 ) throw new SmtpException( "Atleast one recipient must be set." );
82             }
83             
84                     
85             // start with a reset incase old data
86             // is present at the server in this session
87             smtp.WriteRset();
88             
89             // write the mail from command
90             smtp.WriteMailFrom( msg.From.Address );
91             
92             // write the rcpt to command for the To addresses
93             foreach( MailAddress addr in msg.To ) {
94                 smtp.WriteRcptTo( addr.Address );
95             }
96
97             // write the rcpt to command for the Cc addresses
98             foreach( MailAddress addr in msg.Cc ) {
99                 smtp.WriteRcptTo( addr.Address );
100             }
101
102             // write the rcpt to command for the Bcc addresses
103             foreach( MailAddress addr in msg.Bcc ) {
104                 smtp.WriteRcptTo( addr.Address );
105             }
106             
107             // write the data command and then
108             // send the email
109             smtp.WriteData();
110                 
111             if( msg.Attachments.Count == 0 ) {
112                 SendSinglepartMail( msg );          
113             } else {
114                 
115                 SendMultipartMail( msg );
116             
117             }
118
119             // write the data end tag "."
120             smtp.WriteDataEndTag();
121
122         }
123         
124         // sends a single part mail to the server
125         private void SendSinglepartMail( MailMessageWrapper msg ) {
126                             
127             // write the header
128             smtp.WriteHeader( msg.Header );
129             
130             // send the mail body
131             smtp.WriteBytes( msg.BodyEncoding.GetBytes( msg.Body ) );
132
133         }
134
135         // SECURITY-FIXME: lower assertion with imperative asserts      
136         [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
137         // sends a multipart mail to the server
138         private void SendMultipartMail( MailMessageWrapper msg ) {
139                     
140             // generate the boundary between attachments
141             string boundary = MailUtil.GenerateBoundary();
142                 
143             // set the Content-Type header to multipart/mixed
144             string bodyContentType = msg.Header.ContentType;
145
146             msg.Header.ContentType = 
147                 String.Format( "multipart/mixed;\r\n   boundary={0}" , boundary );
148                 
149             // write the header
150             smtp.WriteHeader( msg.Header );
151                 
152             // write the first part text part
153             // before the attachments
154             smtp.WriteBoundary( boundary );
155                 
156             MailHeader partHeader = new MailHeader();
157             partHeader.ContentType = bodyContentType;           
158
159 #if NET_1_1
160                 // Add all the custom headers to body part as specified in 
161                 //Fields property of MailMessageWrapper
162
163         //Remove fields specific for authenticating to SMTP server.
164         //Need to incorporate AUTH command in SmtpStream to handle  
165         //Authorization info. Its a temporary fix for Bug no 68829.
166         //Will dig some more on SMTP AUTH command, and then implement
167         //Authorization. - Sanjay
168
169         if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] != null)
170             msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate");
171         if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"] != null)
172             msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendusername");\r
173         if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"] != null)\r
174             msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendpassword");
175                 partHeader.Data.Add (msg.Fields.Data);
176 #endif
177
178             smtp.WriteHeader( partHeader );
179           
180             // FIXME: probably need to use QP or Base64 on everything higher
181             // then 8-bit .. like utf-16
182             smtp.WriteBytes( msg.BodyEncoding.GetBytes( msg.Body )  );
183
184             smtp.WriteBoundary( boundary );
185
186             // now start to write the attachments
187             
188             for( int i=0; i< msg.Attachments.Count ; i++ ) {
189                 MailAttachment a = (MailAttachment)msg.Attachments[ i ];
190                         
191                 FileInfo fileInfo = new FileInfo( a.Filename );
192
193                 MailHeader aHeader = new MailHeader();
194                 
195                 aHeader.ContentType = 
196                     String.Format (MimeTypes.GetMimeType (fileInfo.Name) + "; name=\"{0}\"",fileInfo.Name);
197                 
198                 aHeader.ContentDisposition = 
199                     String.Format( "attachment; filename=\"{0}\"" , fileInfo.Name );
200                 
201                 aHeader.ContentTransferEncoding = a.Encoding.ToString();
202                                 
203                 smtp.WriteHeader( aHeader );
204                    
205                 // perform the actual writing of the file.
206                 // read from the file stream and write to the tcp stream
207                 FileStream ins = fileInfo.OpenRead ();
208                 
209                 // create an apropriate encoder
210                 IAttachmentEncoder encoder;
211                 if( a.Encoding == MailEncoding.UUEncode ) {
212                     encoder = new UUAttachmentEncoder( 644 , fileInfo.Name  );
213                 } else {
214                     encoder = new Base64AttachmentEncoder();
215                 }
216                 
217                 encoder.EncodeStream( ins , smtp.Stream );
218                 
219                 ins.Close();
220                 
221                     
222                 smtp.WriteLine( "" );
223                 
224                 // if it is the last attachment write
225                 // the final boundary otherwise write
226                 // a normal one.
227                 if( i < (msg.Attachments.Count - 1) ) { 
228                     smtp.WriteBoundary( boundary );
229                 } else {
230                     smtp.WriteFinalBoundary( boundary );
231                 }
232                     
233             }
234                
235         }
236         
237         // send quit command and
238         // closes the connection
239         public void Close() {
240             
241             smtp.WriteQuit();
242             tcpConnection.Close();
243         
244         }
245         
246                 
247     }
248 #if NET_2_0
249 #pragma warning restore 618
250 #endif
251 }