2004-09-09 Tim Coleman <tim@timcoleman.com>
authorTim Coleman <tim@mono-cvs.ximian.com>
Fri, 10 Sep 2004 04:27:21 +0000 (04:27 -0000)
committerTim Coleman <tim@mono-cvs.ximian.com>
Fri, 10 Sep 2004 04:27:21 +0000 (04:27 -0000)
        * AttachmentCollection.cs MailAddressCollection.cs:
                New classes
        * Attachment.cs: Set content string
        * MailMessage.cs: Use new collection classes
        * SmtpClient.cs: Lots of MIME cleanup

svn path=/trunk/mcs/; revision=33690

mcs/class/System/System.Net.Mail/Attachment.cs
mcs/class/System/System.Net.Mail/AttachmentCollection.cs [new file with mode: 0755]
mcs/class/System/System.Net.Mail/ChangeLog
mcs/class/System/System.Net.Mail/MailAddressCollection.cs [new file with mode: 0755]
mcs/class/System/System.Net.Mail/MailMessage.cs
mcs/class/System/System.Net.Mail/SmtpClient.cs

index 8c7c0a0b4a5c5b701b960a5e473f289957bbfd16..e034a154b699bd169654cafe9d8f98aff916a877 100755 (executable)
@@ -187,6 +187,8 @@ namespace System.Net.Mail {
                        sw.Flush ();
 
                        contentStream.Position = 0;
+
+                       this.contentString = contentString;
                }
 
                public void SetContent (string contentString, string mediaType, Encoding encoding, TransferEncoding transferEncoding)
diff --git a/mcs/class/System/System.Net.Mail/AttachmentCollection.cs b/mcs/class/System/System.Net.Mail/AttachmentCollection.cs
new file mode 100755 (executable)
index 0000000..55cf7dd
--- /dev/null
@@ -0,0 +1,50 @@
+//
+// System.Net.Mail.AttachmentCollection.cs
+//
+// Author:
+//     Tim Coleman (tim@timcoleman.com)
+//
+// Copyright (C) Tim Coleman, 2004
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_2_0
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace System.Net.Mail {
+       [CLSCompliant (false)]
+       public class AttachmentCollection : List<Attachment>, IDisposable, ICollection, IEnumerable, IList, ICollection<Attachment>, IEnumerable<Attachment>, IList<Attachment>
+       {
+               [MonoTODO]
+               public void Dispose ()
+               {
+                       for (int i = 0; i < Count; i += 1)
+                               this [i].Dispose ();
+               }
+       }
+}
+
+#endif
index 2ada6c2b6a064683423e08ab100031e8f8b6c4c2..d5907d35264e3b358ad491d254980bcb9ea9a42b 100644 (file)
@@ -1,3 +1,10 @@
+2004-09-09  Tim Coleman <tim@timcoleman.com>
+       * AttachmentCollection.cs MailAddressCollection.cs:
+               New classes
+       * Attachment.cs: Set content string
+       * MailMessage.cs: Use new collection classes
+       * SmtpClient.cs: Lots of MIME cleanup
+
 2004-09-08  Tim Coleman <tim@timcoleman.com>
        * Attachment.cs: Add SetContentFromFile methods
        * MailMessage.cs: Add MIME-Version header
diff --git a/mcs/class/System/System.Net.Mail/MailAddressCollection.cs b/mcs/class/System/System.Net.Mail/MailAddressCollection.cs
new file mode 100755 (executable)
index 0000000..94c8dcb
--- /dev/null
@@ -0,0 +1,83 @@
+//
+// System.Net.Mail.MailAddressCollection.cs
+//
+// Author:
+//     Tim Coleman (tim@timcoleman.com)
+//
+// Copyright (C) Tim Coleman, 2004
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_2_0
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace System.Net.Mail {
+       [CLSCompliant (false)]
+       public class MailAddressCollection : List<MailAddress>, ICollection, IEnumerable, IList, ICollection<MailAddress>, IEnumerable<MailAddress>, IList<MailAddress>
+       {
+               #region Constructors
+
+               internal MailAddressCollection ()
+               {
+               }
+
+               public MailAddressCollection (string addresses)
+               {
+                       AddAddresses (addresses.Split (new char [1] {','}));
+               }
+
+               public MailAddressCollection (string[] addresses)
+               {
+                       AddAddresses (addresses);
+               }
+
+               #endregion
+
+               #region Methods
+
+               private void AddAddresses (string[] addresses)
+               {
+                       foreach (string address in addresses)
+                               Add (new MailAddress (address));
+               }
+
+               public override string ToString ()
+               {
+                       StringBuilder sb = new StringBuilder ();
+                       for (int i = 0; i < Count; i += 1) {
+                               if (i > 0)
+                                       sb.Append (", ");
+                               sb.Append (this [i].ToString ());
+                       }
+                       return sb.ToString ();
+               }
+
+               #endregion
+       }
+}
+
+#endif
index c31a2c35a5494046077482470fa3d13e7b19d0c9..f58915aea7436b4c43a36cfaf624fc941302d9bf 100755 (executable)
@@ -30,7 +30,6 @@
 
 #if NET_2_0
 
-using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.Net.Mime;
 using System.Text;
@@ -41,16 +40,16 @@ namespace System.Net.Mail {
        {
                #region Fields
 
-               Collection<Attachment> alternateViews;
-               Collection<Attachment> attachments;
-               Collection<MailAddress> bcc;
+               AttachmentCollection alternateViews;
+               AttachmentCollection attachments;
+               MailAddressCollection bcc;
                string body;
                ContentType bodyContentType;
                Encoding bodyEncoding;
-               Collection<MailAddress> cc;
+               MailAddressCollection cc;
                MailAddress from;
                NameValueCollection headers;
-               Collection<MailAddress> to;
+               MailAddressCollection to;
                string subject;
                Encoding subjectEncoding;
 
@@ -62,13 +61,13 @@ namespace System.Net.Mail {
                {
                        From = from;
 
-                       this.to = new Collection<MailAddress> ();
+                       this.to = new MailAddressCollection ();
                        this.to.Add (to);
 
-                       alternateViews = new Collection<Attachment> ();
-                       attachments = new Collection<Attachment> ();
-                       bcc = new Collection<MailAddress> ();
-                       cc = new Collection<MailAddress> ();
+                       alternateViews = new AttachmentCollection ();
+                       attachments = new AttachmentCollection ();
+                       bcc = new MailAddressCollection ();
+                       cc = new MailAddressCollection ();
                        bodyContentType = new ContentType (MediaTypeNames.Application.Octet);
                        headers = new NameValueCollection ();
 
@@ -91,15 +90,18 @@ namespace System.Net.Mail {
 
                #region Properties
 
-               public Collection<Attachment> AlternateViews {
+               [CLSCompliant (false)]
+               public AttachmentCollection AlternateViews {
                        get { return alternateViews; }
                }
 
-               public Collection<Attachment> Attachments {
+               [CLSCompliant (false)]
+               public AttachmentCollection Attachments {
                        get { return attachments; }
                }
 
-               public Collection<MailAddress> Bcc {
+               [CLSCompliant (false)]
+               public MailAddressCollection Bcc {
                        get { return bcc; }
                }
 
@@ -120,7 +122,8 @@ namespace System.Net.Mail {
                        }
                }
 
-               public Collection<MailAddress> CC {
+               [CLSCompliant (false)]
+               public MailAddressCollection CC {
                        get { return cc; }
                }
 
@@ -143,7 +146,8 @@ namespace System.Net.Mail {
                        set { subjectEncoding = value; }
                }
 
-               public Collection<MailAddress> To {
+               [CLSCompliant (false)]
+               public MailAddressCollection To {
                        get { return to; }
                }
 
index 46a2388ddec9852f389177e7d5370a48d6f00117..d447cd3e32ff661fce142467ef3b997a9a92e48b 100755 (executable)
 
 #if NET_2_0
 
-using System.Collections.Generic;
+using System;
 using System.ComponentModel;
 using System.IO;
 using System.Net;
 using System.Net.Mime;
 using System.Net.Sockets;
 using System.Text;
+using System.Threading;
 
 namespace System.Net.Mail {
        public class SmtpClient : IDisposable //, IGetContextAwareResult
@@ -48,7 +49,14 @@ namespace System.Net.Mail {
                int timeout;
                ICredentialsByHost credentials;
                bool useDefaultCredentials;
+
                TcpClient client;
+               NetworkStream stream;
+               StreamWriter writer;
+               StreamReader reader;
+               int boundaryIndex;
+
+               Mutex mutex = new Mutex ();
 
                const string MimeVersion = "1.0 (produced by Mono System.Net.Mail.SmtpClient)";
 
@@ -132,31 +140,26 @@ namespace System.Net.Mail {
                {
                }
 
-               private bool IsError (SmtpResponse status)
+               private void EndSection (string section)
                {
-                       return ((int) status.StatusCode) >= 400;
+                       SendData (String.Format ("--{0}--", section));
                }
 
-               public static string GenerateBoundary() 
+               private string GenerateBoundary ()
                {
-                       StringBuilder  boundary = new StringBuilder ("__MONO__Boundary");
-
-                       boundary.Append ("__");
-
-                       DateTime now = DateTime.Now;
-                       boundary.Append (now.Year);
-                       boundary.Append (now.Month);
-                       boundary.Append (now.Day);
-                       boundary.Append (now.Hour);
-                       boundary.Append (now.Minute);
-                       boundary.Append (now.Second);
-                       boundary.Append (now.Millisecond);
+                       string output = GenerateBoundary (boundaryIndex);
+                       boundaryIndex += 1;
+                       return output;
+               }
 
-                       boundary.Append ("__");
-                       boundary.Append ((new Random ()).Next ());
-                       boundary.Append ("__");
+               private static string GenerateBoundary (int index)
+               {
+                       return String.Format ("--boundary_{0}_{1}", index, Guid.NewGuid ().ToString ("D"));
+               }
 
-                       return boundary.ToString();
+               private bool IsError (SmtpResponse status)
+               {
+                       return ((int) status.StatusCode) >= 400;
                }
 
                protected void OnSendCompleted (AsyncCompletedEventArgs e)
@@ -165,132 +168,179 @@ namespace System.Net.Mail {
                                SendCompleted (this, e);
                }
 
+               private SmtpResponse Read ()
+               {
+                       SmtpResponse response;
+
+                       char[] buf = new char [3];
+                       reader.Read (buf, 0, 3);
+                       reader.Read ();
+
+                       response.StatusCode = (SmtpStatusCode) Int32.Parse (new String (buf));
+                       response.Description = reader.ReadLine ();
+
+                       return response;
+               }
+
                [MonoTODO ("Need to work on message attachments.")]
                public void Send (MailMessage message)
                {
+                       // Block while sending
+                       mutex.WaitOne ();
+
                        SmtpResponse status;
-                       Sender sender = new Sender (Host, Port);
 
-                       string hostname = Dns.GetHostName ();
+                       client = new TcpClient (host, port);
+                       stream = client.GetStream ();
+                       writer = new StreamWriter (stream);
+                       reader = new StreamReader (stream);
+                       boundaryIndex = 0;
                        string boundary = GenerateBoundary ();
-                       string messageID = String.Format ("<{0}@{1}>", Guid.NewGuid ().ToString ("n").ToUpper (), hostname);
 
-                       status = sender.Read ();
+                       bool hasAlternateViews = (message.AlternateViews.Count > 0);
+                       bool hasAttachments = (message.Attachments.Count > 0);
+
+                       status = Read ();
                        if (IsError (status))
                                throw new SmtpException (status.StatusCode);
 
                        // HELO
-                       status = sender.SendCommand (Command.Helo, hostname);
+                       status = SendCommand (Command.Helo, Dns.GetHostName ());
                        if (IsError (status))
                                throw new SmtpException (status.StatusCode);
 
                        // MAIL FROM:
-                       status = sender.SendCommand (Command.MailFrom, message.From.Address);
+                       status = SendCommand (Command.MailFrom, message.From.Address);
                        if (IsError (status))
                                throw new SmtpException (status.StatusCode);
 
                        // Send RCPT TO: for all recipients in the To list
                        for (int i = 0; i < message.To.Count; i += 1) {
-                               status = sender.SendCommand (Command.RcptTo, message.To [i].Address);
+                               status = SendCommand (Command.RcptTo, message.To [i].Address);
                                if (IsError (status))
                                        throw new SmtpException (status.StatusCode);
                        }
 
                        // Send RCPT TO: for all recipients in the CC list
                        for (int i = 0; i < message.CC.Count; i += 1) {
-                               status = sender.SendCommand (Command.RcptTo, message.CC [i].Address);
+                               status = SendCommand (Command.RcptTo, message.CC [i].Address);
                                if (IsError (status))
                                        throw new SmtpException (status.StatusCode);
                        }
 
                        // Send RCPT TO: for all recipients in the Bcc list
                        for (int i = 0; i < message.Bcc.Count; i += 1) {
-                               status = sender.SendCommand (Command.RcptTo, message.Bcc [i].Address);
+                               status = SendCommand (Command.RcptTo, message.Bcc [i].Address);
                                if (IsError (status))
                                        throw new SmtpException (status.StatusCode);
                        }
 
                        // DATA
-                       status = sender.SendCommand (Command.Data);
+                       status = SendCommand (Command.Data);
                        if (IsError (status))
                                throw new SmtpException (status.StatusCode);
 
+                       // Figure out the message content type
+                       ContentType messageContentType = message.BodyContentType;
+                       if (hasAttachments || hasAlternateViews) {
+                               messageContentType = new ContentType ();
+                               messageContentType.Boundary = boundary;
+
+                               if (hasAttachments)
+                                       messageContentType.MediaType = "multipart/mixed";
+                               else
+                                       messageContentType.MediaType = "multipart/alternative";
+                       }
+
                        // Send message headers
-                       sender.SendHeader (HeaderName.From, message.From.ToString ());
-                       sender.SendHeader (HeaderName.To, CreateAddressList (message.To));
+                       SendHeader (HeaderName.From, message.From.ToString ());
+                       SendHeader (HeaderName.To, message.To.ToString ());
                        if (message.CC.Count > 0)
-                               sender.SendHeader (HeaderName.Cc, CreateAddressList (message.CC));
+                               SendHeader (HeaderName.Cc, message.CC.ToString ());
                        if (message.Bcc.Count > 0)
-                               sender.SendHeader (HeaderName.Bcc, CreateAddressList (message.Bcc));
-                       sender.SendHeader (HeaderName.Subject, message.Subject);
-                       sender.SendHeader (HeaderName.MessageId, messageID);
+                               SendHeader (HeaderName.Bcc, message.Bcc.ToString ());
+                       SendHeader (HeaderName.Subject, message.Subject);
 
                        foreach (string s in message.Headers.AllKeys)
-                               sender.SendHeader (s, message.Headers [s]);
-
-                       bool isMultipart = (message.Attachments.Count > 0 || message.AlternateViews.Count > 0);
-
-                       if (isMultipart) {
-                               ContentType contentType = new ContentType ();
+                               SendHeader (s, message.Headers [s]);
+
+                       SendHeader ("Content-Type", messageContentType.ToString ());
+                       SendData ("");
+
+                       if (hasAlternateViews) {
+                               string innerBoundary = boundary;
+
+                               // The body is *technically* an alternative view.  The body text goes FIRST because
+                               // that is most compatible with non-MIME readers.
+                               //
+                               // If there are attachments, then the main content-type is multipart/mixed and
+                               // the subpart has type multipart/alternative.  Then all of the views have their
+                               // own types.  
+                               //
+                               // If there are no attachments, then the main content-type is multipart/alternative
+                               // and we don't need this subpart.
+
+                               if (hasAttachments) {
+                                       innerBoundary = GenerateBoundary ();
+                                       ContentType contentType = new ContentType ("multipart/alternative");
+                                       contentType.Boundary = innerBoundary;
+                                       StartSection (boundary, contentType);
+                               }
+                               
+                               // Start the section for the body text.  This is either section "1" or "0" depending
+                               // on whether there are attachments.
 
-                               contentType.Boundary = boundary;
+                               StartSection (innerBoundary, message.BodyContentType, TransferEncoding.QuotedPrintable);
+                               SendData (message.Body);
 
-                               if (message.Attachments.Count > 0)
-                                       contentType.MediaType = "multipart/mixed";
-                               else
-                                       contentType.MediaType = "multipart/related";
+                               // Send message attachments.
+                               SendAttachments (message.AlternateViews, innerBoundary);
 
-                               sender.SendHeader ("Content-Type", contentType.ToString ());
-                               sender.StartSection (boundary, message.BodyContentType);
+                               if (hasAttachments) 
+                                       EndSection (innerBoundary);
                        }
                        else {
-                               sender.SendHeader ("Content-Type", message.BodyContentType.ToString ());
-                               sender.Send ("");
+                               // If this is multipart then we need to send a boundary before the body.
+                               if (hasAttachments)
+                                       StartSection (boundary, message.BodyContentType, TransferEncoding.QuotedPrintable);
+                               SendData (message.Body);
                        }
 
-                       sender.Send (message.Body);
+                       // Send attachments
+                       if (hasAttachments) {
+                               string innerBoundary = boundary;
 
-                       if (message.AlternateViews.Count > 0) {
-                               ContentType contentType = new ContentType ("multipart/related");
-                               contentType.Boundary = GenerateBoundary ();
-                               sender.StartSection (boundary, contentType);
-                               SendAttachments (sender, message.AlternateViews, contentType);
-                       }
+                               // If we have alternate views and attachments then we need to nest this part inside another
+                               // boundary.  Otherwise, we are cool with the boundary we have.
 
-                       if (message.Attachments.Count > 0) {
-                               ContentType contentType = new ContentType ("multipart/mixed");
-                               contentType.Boundary = GenerateBoundary ();
-                               sender.StartSection (boundary, contentType);
-                               SendAttachments (sender, message.Attachments, contentType);
-                       }
+                               if (hasAlternateViews) {
+                                       innerBoundary = GenerateBoundary ();
+                                       ContentType contentType = new ContentType ("multipart/mixed");
+                                       contentType.Boundary = innerBoundary;
+                                       StartSection (boundary, contentType);
+                               }
 
-                       if (isMultipart)
-                               sender.EndSection (boundary);
+                               SendAttachments (message.Attachments, innerBoundary);
 
-                       sender.Send (".");
+                               if (hasAlternateViews)
+                                       EndSection (innerBoundary);
+                       }
 
-                       status = sender.Read ();
+                       SendData (".");
+
+                       status = Read ();
                        if (IsError (status))
                                throw new SmtpException (status.StatusCode);
 
-                       status = sender.SendCommand (Command.Quit);
-
-                       sender.Close ();
-               }
+                       status = SendCommand (Command.Quit);
 
-               private string CreateAddressList (Collection<MailAddress> addressList)
-               {
-                       if (addressList.Count > 0) {
-                               StringBuilder sb = new StringBuilder ();
-                               for (int i = 0; i < addressList.Count; i += 1) {
-                                       if (sb.Length > 0)
-                                               sb.Append (", ");
-                                       sb.Append (addressList [i].ToString ());
-                               }
-                               return sb.ToString ();
-                       }
+                       writer.Close ();
+                       reader.Close ();
+                       stream.Close ();
+                       client.Close ();
 
-                       return null;
+                       // Release the mutex to allow other threads access
+                       mutex.ReleaseMutex ();
                }
 
                public void Send (string from, string to, string subject, string body)
@@ -298,6 +348,12 @@ namespace System.Net.Mail {
                        Send (new MailMessage (from, to, subject, body));
                }
 
+               private void SendData (string data)
+               {
+                       writer.WriteLine (data);
+                       writer.Flush ();
+               }
+
                [MonoTODO]
                public void SendAsync (MailMessage message, object userToken)
                {
@@ -316,13 +372,97 @@ namespace System.Net.Mail {
                        throw new NotImplementedException ();
                }
 
-               private void SendAttachments (Sender sender, Collection<Attachment> attachments, ContentType contentType)
+               private void SendAttachments (AttachmentCollection attachments, string boundary)
                {
                        for (int i = 0; i < attachments.Count; i += 1) {
-                               sender.StartSection (contentType.Boundary, attachments [i].ContentType);
-                               sender.Send ("TO BE IMPLEMENTED");
+                               StartSection (boundary, attachments [i].ContentType, attachments [i].TransferEncoding);
+
+                               switch (attachments [i].TransferEncoding) {
+                               case TransferEncoding.Base64:
+                                       StreamReader reader = new StreamReader (attachments [i].ContentStream);
+                                       byte[] content = new byte [attachments [i].ContentStream.Length];
+                                       attachments [i].ContentStream.Read (content, 0, content.Length);
+                                       SendData (Convert.ToBase64String (content, Base64FormattingOptions.InsertLineBreaks));
+                                       break;
+                               case TransferEncoding.QuotedPrintable:
+                                       SendData (ToQuotedPrintable (attachments [i].ContentString));
+                                       break;
+                               default:
+                                       SendData ("TO BE IMPLEMENTED");
+                                       break;
+                               }
                        }
-                       sender.EndSection (contentType.Boundary);
+               }
+
+               private SmtpResponse SendCommand (string command, string data)
+               {
+                       SmtpResponse response;
+                       writer.Write (command);
+                       writer.Write (" ");
+                       SendData (data);
+                       return Read ();
+               }
+
+               private SmtpResponse SendCommand (string command)
+               {
+                       writer.WriteLine (command);
+                       writer.Flush ();
+                       return Read ();
+               }
+
+               private void SendHeader (string name, string value)
+               {
+                       SendData (String.Format ("{0}: {1}", name, value));
+               }
+
+               private void StartSection (string section, ContentType sectionContentType)
+               {
+                       SendData (String.Format ("--{0}", section));
+                       SendHeader ("content-type", sectionContentType.ToString ());
+                       SendData ("");
+               }
+
+               private void StartSection (string section, ContentType sectionContentType, TransferEncoding transferEncoding)
+               {
+                       SendData (String.Format ("--{0}", section));
+                       SendHeader ("content-type", sectionContentType.ToString ());
+                       SendHeader ("content-transfer-encoding", GetTransferEncodingName (transferEncoding));
+                       SendData ("");
+               }
+
+               private string ToQuotedPrintable (string input)
+               {
+                       StringReader reader = new StringReader (input);
+                       StringWriter writer = new StringWriter ();
+                       int i;
+
+                       while ((i = reader.Read ()) > 0) {
+                               if (i > 127) {
+                                       writer.Write ("=");
+                                       writer.Write (Convert.ToString (i, 16).ToUpper ());
+                               }
+                               else
+                                       writer.Write (Convert.ToChar (i));
+                       }
+
+                       return writer.GetStringBuilder ().ToString ();
+               }
+
+               private static string GetTransferEncodingName (TransferEncoding encoding)
+               {
+                       switch (encoding) {
+                       case TransferEncoding.QuotedPrintable:
+                               return "quoted-printable";
+                       case TransferEncoding.EightBit:
+                               return "8bit";
+                       case TransferEncoding.SevenBit:
+                               return "7bit";
+                       case TransferEncoding.Base64:
+                               return "base64";
+                       case TransferEncoding.Binary:
+                               return "binary";
+                       }
+                       return "unknown";
                }
 
 /*
@@ -361,97 +501,6 @@ namespace System.Net.Mail {
                        public SmtpStatusCode StatusCode;
                        public string Description;
                }
-
-               // The Sender class is used to manage sending information to the SMTP server.
-               private class Sender
-               {
-                       #region Fields
-
-                       TcpClient client;
-                       NetworkStream stream;
-                       StreamWriter writer;
-                       StreamReader reader;
-
-                       #endregion // Fields
-
-                       #region Constructors
-
-                       public Sender (String host, int port)
-                       {
-                               client = new TcpClient (host, port);
-                               stream = client.GetStream ();
-                               writer = new StreamWriter (stream);
-                               reader = new StreamReader (stream);
-                       }
-
-                       #endregion // Constructors
-
-                       #region Methods
-
-                       public void Close ()
-                       {
-                               writer.Close ();
-                               reader.Close ();
-                               stream.Close ();
-                               client.Close ();
-                       }
-
-                       public void EndSection (string section)
-                       {
-                               Send (String.Format ("--{0}--", section));
-                       }
-
-                       public void Send (string data)
-                       {
-                               writer.WriteLine (data);
-                               writer.Flush ();
-                       }
-
-                       public SmtpResponse SendCommand (string command)
-                       {
-                               writer.WriteLine (command);
-                               writer.Flush ();
-                               return Read ();
-                       }
-
-                       public SmtpResponse SendCommand (string command, string data)
-                       {
-                               SmtpResponse response;
-                               writer.Write (command);
-                               writer.Write (" ");
-                               Send (data);
-
-                               return Read ();
-                       }
-
-                       public void SendHeader (string name, string value)
-                       {
-                               Send (String.Format ("{0}: {1}", name, value));
-                       }
-
-                       public SmtpResponse Read ()
-                       {
-                               SmtpResponse response;
-
-                               char[] buf = new char [3];
-                               reader.Read (buf, 0, 3);
-                               reader.Read ();
-
-                               response.StatusCode = (SmtpStatusCode) Int32.Parse (new String (buf));
-                               response.Description = reader.ReadLine ();
-
-                               return response;
-                       }
-
-                       public void StartSection (string section, ContentType sectionContentType)
-                       {
-                               Send (String.Format ("--{0}", section));
-                               SendHeader ("Content-Type", sectionContentType.ToString ());
-                               Send ("");
-                       }
-
-                       #endregion // Methods
-               }
        }
 }