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
;
37 namespace System
.Web
.Mail
{
39 /// represents a conntection to a smtp server
40 internal class SmtpClient
{
42 private string server
;
43 private TcpClient tcpConnection
;
44 private SmtpStream smtp
;
45 private Encoding encoding
;
47 //Initialise the variables and connect
48 public SmtpClient( string server
) {
51 encoding
= new ASCIIEncoding( );
56 // make the actual connection
57 // and HELO handshaking
58 private void Connect() {
59 tcpConnection
= new TcpClient( server
, 25 );
61 Stream stream
= tcpConnection
.GetStream();
62 smtp
= new SmtpStream( stream
);
64 // read the server greeting
66 smtp
.CheckForStatusCode( 220 );
68 // write the HELO command to the server
69 smtp
.WriteHelo( Dns
.GetHostName() );
73 public void Send( MailMessageWrapper msg
) {
75 if( msg
.From
== null ) {
76 throw new SmtpException( "From property must be set." );
79 if( msg
.To
== null ) {
80 if( msg
.To
.Count
< 1 ) throw new SmtpException( "Atleast one recipient must be set." );
84 // start with a reset incase old data
85 // is present at the server in this session
88 // write the mail from command
89 smtp
.WriteMailFrom( msg
.From
.Address
);
91 // write the rcpt to command for the To addresses
92 foreach( MailAddress addr
in msg
.To
) {
93 smtp
.WriteRcptTo( addr
.Address
);
96 // write the rcpt to command for the Cc addresses
97 foreach( MailAddress addr
in msg
.Cc
) {
98 smtp
.WriteRcptTo( addr
.Address
);
101 // write the rcpt to command for the Bcc addresses
102 foreach( MailAddress addr
in msg
.Bcc
) {
103 smtp
.WriteRcptTo( addr
.Address
);
106 // write the data command and then
110 if( msg
.Attachments
.Count
== 0 ) {
112 //The message might be multipart, if RelatedBodyParts are present
113 if (msg
.RelatedBodyParts
.Count
!= 0)
114 SendMultipartMail (msg
);
117 SendSinglepartMail( msg
);
120 SendMultipartMail( msg
);
124 // write the data end tag "."
125 smtp
.WriteDataEndTag();
129 // sends a single part mail to the server
130 private void SendSinglepartMail( MailMessageWrapper msg
) {
133 smtp
.WriteHeader( msg
.Header
);
135 // send the mail body
136 smtp
.WriteBytes( msg
.BodyEncoding
.GetBytes( msg
.Body
) );
140 // sends a multipart mail to the server
141 private void SendMultipartMail( MailMessageWrapper msg
) {
143 // generate the boundary between attachments
144 string boundary
= MailUtil
.GenerateBoundary();
146 // set the Content-Type header to multipart/mixed
147 string bodyContentType
= msg
.Header
.ContentType
;
150 if (msg
.RelatedBodyParts
.Count
!= 0)
151 msg
.Header
.ContentType
= String
.Format( "multipart/related;\r\n boundary={0}" , boundary
);
154 msg
.Header
.ContentType
=
155 String
.Format( "multipart/mixed;\r\n boundary={0}" , boundary
);
158 smtp
.WriteHeader( msg
.Header
);
160 // write the first part text part
161 // before the attachments
162 smtp
.WriteBoundary( boundary
);
164 MailHeader partHeader
= new MailHeader();
165 partHeader
.ContentType
= bodyContentType
;
168 // Add all the custom headers to body part as specified in
169 //Fields property of MailMessageWrapper
171 //Remove fields specific for authenticating to SMTP server.
172 //Need to incorporate AUTH command in SmtpStream to handle
173 //Authorization info. Its a temporary fix for Bug no 68829.
174 //Will dig some more on SMTP AUTH command, and then implement
175 //Authorization. - Sanjay
177 if (msg
.Fields
.Data
["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] != null)
178 msg
.Fields
.Data
.Remove ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate");
179 if (msg
.Fields
.Data
["http://schemas.microsoft.com/cdo/configuration/sendusername"] != null)
180 msg
.Fields
.Data
.Remove ("http://schemas.microsoft.com/cdo/configuration/sendusername");
181 if (msg
.Fields
.Data
["http://schemas.microsoft.com/cdo/configuration/sendpassword"] != null)
182 msg
.Fields
.Data
.Remove ("http://schemas.microsoft.com/cdo/configuration/sendpassword");
183 partHeader
.Data
.Add (msg
.Fields
.Data
);
186 smtp
.WriteHeader( partHeader
);
188 // FIXME: probably need to use QP or Base64 on everything higher
189 // then 8-bit .. like utf-16
190 smtp
.WriteBytes( msg
.BodyEncoding
.GetBytes( msg
.Body
) );
192 smtp
.WriteBoundary( boundary
);
195 for (int i
= 0; i
< msg
.RelatedBodyParts
.Count
; i
++) {
196 RelatedBodyPart rbp
= (RelatedBodyPart
) msg
.RelatedBodyParts
[i
];
197 FileInfo file
= new FileInfo (rbp
.Path
);
198 MailHeader header
= new MailHeader ();
199 header
.ContentLocation
= rbp
.Path
;
200 header
.ContentType
= String
.Format (MimeTypes
.GetMimeType (file
.Name
) + "; name=\"{0}\"",file
.Name
);
201 //If content id and ContentLocation both are present
202 //in mime header of a mail, than RelatedBodyPart of mail
203 //doesnt show up in a machine other than from which mail
204 //was sent, and hence only one of them is inserted in
205 //header. Need to check how the things go when another
206 //body part refers the content with the content id specified
207 /*if (rbp.Name != null)
208 header.Data.Add ("Content-ID", "<"+rbp.Name+">");*/
210 header
.ContentTransferEncoding
= "Base64";
211 header
.ContentDisposition
= String
.Format( "inline; filename=\"{0}\"" , file
.Name
);
213 smtp
.WriteHeader (header
);
214 FileStream rbpStream
= new FileStream (file
.FullName
, FileMode
.Open
);
215 IAttachmentEncoder rbpEncoder
= new Base64AttachmentEncoder ();
216 rbpEncoder
.EncodeStream (rbpStream
, smtp
.Stream
);
218 smtp
.WriteLine( "" );
220 if (i
< (msg
.RelatedBodyParts
.Count
- 1)) {
221 smtp
.WriteBoundary (boundary
);
223 if (msg
.Attachments
.Count
== 0)
224 smtp
.WriteFinalBoundary (boundary
);
226 smtp
.WriteBoundary (boundary
);
231 // now start to write the attachments
233 for( int i
=0; i
< msg
.Attachments
.Count
; i
++ ) {
234 MailAttachment a
= (MailAttachment
)msg
.Attachments
[ i
];
236 FileInfo fileInfo
= new FileInfo( a
.Filename
);
238 MailHeader aHeader
= new MailHeader();
240 aHeader
.ContentType
=
241 String
.Format (MimeTypes
.GetMimeType (fileInfo
.Name
) + "; name=\"{0}\"",fileInfo
.Name
);
243 aHeader
.ContentDisposition
=
244 String
.Format( "attachment; filename=\"{0}\"" , fileInfo
.Name
);
246 aHeader
.ContentTransferEncoding
= a
.Encoding
.ToString();
248 smtp
.WriteHeader( aHeader
);
250 // perform the actual writing of the file.
251 // read from the file stream and write to the tcp stream
252 FileStream ins
= new FileStream( fileInfo
.FullName
, FileMode
.Open
);
254 // create an apropriate encoder
255 IAttachmentEncoder encoder
;
256 if( a
.Encoding
== MailEncoding
.UUEncode
) {
257 encoder
= new UUAttachmentEncoder( 644 , fileInfo
.Name
);
259 encoder
= new Base64AttachmentEncoder();
262 encoder
.EncodeStream( ins
, smtp
.Stream
);
267 smtp
.WriteLine( "" );
269 // if it is the last attachment write
270 // the final boundary otherwise write
272 if( i
< (msg
.Attachments
.Count
- 1) ) {
273 smtp
.WriteBoundary( boundary
);
275 smtp
.WriteFinalBoundary( boundary
);
282 // send quit command and
283 // closes the connection
284 public void Close() {
287 tcpConnection
.Close();