// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-#if NET_2_0
-
#if SECURITY_DEP
+
+#if MONOTOUCH || MONODROID
+using System.Security.Cryptography.X509Certificates;
+#else
extern alias PrebuiltSystem;
+using X509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
+using System.Security.Cryptography.X509Certificates;
+#endif
+
#endif
using System;
using System.Net;
using System.Net.Mime;
using System.Net.Sockets;
-using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Configuration;
using System.Net.Security;
using System.Security.Authentication;
-
-#if SECURITY_DEP
-using X509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
+#if NET_4_5
+using System.Threading.Tasks;
#endif
namespace System.Net.Mail {
public class SmtpClient
+#if NET_4_0
+ : IDisposable
+#endif
{
#region Fields
if (cfg != null) {
this.host = cfg.Network.Host;
this.port = cfg.Network.Port;
+#if NET_4_0
+ this.enableSsl = cfg.Network.EnableSsl;
+#endif
TargetName = cfg.Network.TargetName;
if (this.TargetName == null)
TargetName = "SMTPSVC/" + (host != null ? host : "");
Credentials = new CCredentialsByHost (cfg.Network.UserName, password);
}
- if (cfg.From != null)
+ if (!String.IsNullOrEmpty (cfg.From))
defaultFrom = new MailAddress (cfg.From);
}
#else
if (port != 0)
this.port = port;
+ else if (this.port == 0)
+ this.port = 25;
}
#endregion // Constructors
#endregion // Events
#region Methods
+#if NET_4_0
+ public void Dispose ()
+ {
+ Dispose (true);
+ }
+ [MonoTODO ("Does nothing at the moment.")]
+ protected virtual void Dispose (bool disposing)
+ {
+ // TODO: We should close all the connections and abort any async operations here
+ }
+#endif
private void CheckState ()
{
if (messageInProcess != null)
private static string EncodeAddress(MailAddress address)
{
- string encodedDisplayName = ContentType.EncodeSubjectRFC2047 (address.DisplayName, Encoding.UTF8);
- return "\"" + encodedDisplayName + "\" <" + address.Address + ">";
+ if (!String.IsNullOrEmpty (address.DisplayName)) {
+ string encodedDisplayName = ContentType.EncodeSubjectRFC2047 (address.DisplayName, Encoding.UTF8);
+ return "\"" + encodedDisplayName + "\" <" + address.Address + ">";
+ }
+ return address.ToString ();
}
private static string EncodeAddresses(MailAddressCollection addresses)
try {
writer = new StreamWriter(filename);
+ // FIXME: See how Microsoft fixed the bug about envelope senders, and how it actually represents the info in .eml file headers
+ // For all we know, defaultFrom may be the envelope sender
+ // For now, we are no worse than some versions of .NET
MailAddress from = message.From;
-
if (from == null)
from = defaultFrom;
-
- SendHeader (HeaderName.Date, DateTime.Now.ToString ("ddd, dd MMM yyyy HH':'mm':'ss zzz", DateTimeFormatInfo.InvariantInfo));
+
+ string dt = DateTime.Now.ToString("ddd, dd MMM yyyy HH':'mm':'ss zzz", DateTimeFormatInfo.InvariantInfo);
+ // remove ':' from time zone offset (e.g. from "+01:00")
+ dt = dt.Remove(dt.Length - 3, 1);
+ SendHeader(HeaderName.Date, dt);
+
SendHeader (HeaderName.From, EncodeAddress(from));
SendHeader (HeaderName.To, EncodeAddresses(message.To));
if (message.CC.Count > 0)
if (authMechs != AuthMechs.None)
Authenticate ();
-
- MailAddress from = message.From;
- if (from == null)
- from = defaultFrom;
+ // The envelope sender: use 'Sender:' in preference of 'From:'
+ MailAddress sender = message.Sender;
+ if (sender == null)
+ sender = message.From;
+ if (sender == null)
+ sender = defaultFrom;
// MAIL FROM:
- status = SendCommand ("MAIL FROM:<" + from.Address + '>');
+ status = SendCommand ("MAIL FROM:<" + sender.Address + '>');
if (IsError (status)) {
throw new SmtpException (status.StatusCode, status.Description);
}
dt = dt.Remove (dt.Length - 3, 1);
SendHeader (HeaderName.Date, dt);
+ MailAddress from = message.From;
+ if (from == null)
+ from = defaultFrom;
+
SendHeader (HeaderName.From, EncodeAddress (from));
SendHeader (HeaderName.To, EncodeAddresses (message.To));
if (message.CC.Count > 0)
Send (new MailMessage (from, to, subject, body));
}
+#if NET_4_5
+ public Task SendMailAsync (MailMessage message)
+ {
+ var tcs = new TaskCompletionSource<object> ();
+ SendCompletedEventHandler handler = null;
+ handler = (s, e) => SendMailAsyncCompletedHandler (tcs, e, handler, this);
+ SendCompleted += handler;
+ SendAsync (message, tcs);
+ return tcs.Task;
+ }
+
+ public Task SendMailAsync (string from, string recipients, string subject, string body)
+ {
+ return SendMailAsync (new MailMessage (from, recipients, subject, body));
+ }
+
+ static void SendMailAsyncCompletedHandler (TaskCompletionSource<object> source, AsyncCompletedEventArgs e, SendCompletedEventHandler handler, SmtpClient client)
+ {
+ if (handler != e.UserState)
+ return;
+
+ client.SendCompleted -= handler;
+
+ if (e.Error != null) {
+ source.SetException (e.Error);
+ return;
+ }
+
+ if (e.Cancelled) {
+ source.SetCanceled ();
+ return;
+ }
+
+ source.SetResult (null);
+ }
+#endif
+
private void SendDot()
{
writer.Write(".\r\n");
contentType.Parameters ["type"] = av.ContentType.ToString ();
StartSection (inner_boundary, contentType);
- StartSection (alt_boundary, av.ContentType, av.TransferEncoding);
+ StartSection (alt_boundary, av.ContentType, av);
} else {
contentType = new ContentType (av.ContentType.ToString ());
- StartSection (inner_boundary, contentType, av.TransferEncoding);
+ StartSection (inner_boundary, contentType, av);
}
switch (av.TransferEncoding) {
private void SendLinkedResources (MailMessage message, LinkedResourceCollection resources, string boundary)
{
foreach (LinkedResource lr in resources) {
- StartSection (boundary, lr.ContentType, lr.TransferEncoding, lr);
+ StartSection (boundary, lr.ContentType, lr);
switch (lr.TransferEncoding) {
case TransferEncoding.Base64:
contentType.CharSet = att.NameEncoding.HeaderName;
att.ContentDisposition.FileName = att.Name;
}
- StartSection (boundary, contentType, att.TransferEncoding, att == body ? null : att.ContentDisposition);
+ StartSection (boundary, contentType, att, att != body);
byte [] content = new byte [att.ContentStream.Length];
att.ContentStream.Read (content, 0, content.Length);
SendData (string.Empty);
}
- private void StartSection (string section, ContentType sectionContentType,TransferEncoding transferEncoding)
+ private void StartSection (string section, ContentType sectionContentType, AttachmentBase att)
{
SendData (String.Format ("--{0}", section));
SendHeader ("content-type", sectionContentType.ToString ());
- SendHeader ("content-transfer-encoding", GetTransferEncodingName (transferEncoding));
+ SendHeader ("content-transfer-encoding", GetTransferEncodingName (att.TransferEncoding));
+ if (!string.IsNullOrEmpty (att.ContentId))
+ SendHeader("content-ID", "<" + att.ContentId + ">");
SendData (string.Empty);
}
- private void StartSection(string section, ContentType sectionContentType, TransferEncoding transferEncoding, LinkedResource lr)
- {
- SendData (String.Format("--{0}", section));
- SendHeader ("content-type", sectionContentType.ToString ());
- SendHeader ("content-transfer-encoding", GetTransferEncodingName (transferEncoding));
-
- if (lr.ContentId != null && lr.ContentId.Length > 0)
- SendHeader("content-ID", "<" + lr.ContentId + ">");
-
- SendData (string.Empty);
- }
-
- private void StartSection (string section, ContentType sectionContentType, TransferEncoding transferEncoding, ContentDisposition contentDisposition) {
+ private void StartSection (string section, ContentType sectionContentType, Attachment att, bool sendDisposition) {
SendData (String.Format ("--{0}", section));
+ if (!string.IsNullOrEmpty (att.ContentId))
+ SendHeader("content-ID", "<" + att.ContentId + ">");
SendHeader ("content-type", sectionContentType.ToString ());
- SendHeader ("content-transfer-encoding", GetTransferEncodingName (transferEncoding));
- if (contentDisposition != null)
- SendHeader ("content-disposition", contentDisposition.ToString ());
+ SendHeader ("content-transfer-encoding", GetTransferEncodingName (att.TransferEncoding));
+ if (sendDisposition)
+ SendHeader ("content-disposition", att.ContentDisposition.ToString ());
SendData (string.Empty);
}
-
+
// use proper encoding to escape input
private string ToQuotedPrintable (string input, Encoding enc)
{
}
}
-#endif // NET_2_0