diff --git a/ICD.Common.Utils/Email/EmailClient.cs b/ICD.Common.Utils/Email/EmailClient.cs new file mode 100644 index 0000000..ae64ea2 --- /dev/null +++ b/ICD.Common.Utils/Email/EmailClient.cs @@ -0,0 +1,91 @@ +#if SIMPLSHARP +using Crestron.SimplSharp; +#else +using System.Net.Mail; +#endif +using ICD.Common.Properties; + +namespace ICD.Common.Utils.Email +{ + public sealed class EmailClient + { + private readonly EmailStringCollection m_To; + private readonly EmailStringCollection m_Cc; + private readonly EmailStringCollection m_Bcc; + + #region Properties + + public string Host { get; set; } + public ushort Port { get; set; } + public bool Secure { get; set; } + public string Username { get; set; } + public string Password { get; set; } + public string From { get; set; } + public EmailStringCollection To { get { return m_To; } } + public EmailStringCollection Cc { get { return m_Cc; } } + public EmailStringCollection Bcc { get { return m_Bcc; } } + + #endregion + + /// + /// Constructor. + /// + public EmailClient() + { + m_To = new EmailStringCollection(); + m_Cc = new EmailStringCollection(); + m_Bcc = new EmailStringCollection(); + } + + #region Methods + + /// + /// Sends the email. + /// + /// + /// + /// Error codes. + [PublicAPI] + public eMailErrorCode Send(string subject, string message) + { +#if SIMPLSHARP + var response = CrestronMailFunctions.SendMail(Host, Port, Secure, Username, Password, From, m_To.ToString(), "", + m_Cc.ToString(), m_Bcc.ToString(), subject, message, + CrestronMailFunctions.eMailPriority.Normal, 0, ""); + + return MailErrorCodeUtils.FromSimplMailCode(response); +#else + try + { + MailMessage mailMessage = new MailMessage + { + From = new MailAddress(From), + To = {m_To.ToString()}, + CC = {m_Cc.ToString()}, + Bcc = {m_Bcc.ToString()}, + Subject = subject, + Body = message + }; + + SmtpClient client = new SmtpClient + { + Port = Port, + DeliveryMethod = SmtpDeliveryMethod.Network, + UseDefaultCredentials = false, + Host = Host + }; + + client.Send(mailMessage); + } + catch (SmtpException ex) + { + return MailErrorCodeUtils.FromNetStandardMailCode(ex.StatusCode); + } + + return eMailErrorCode.Ok; +#endif + } + + #endregion + } +} diff --git a/ICD.Common.Utils/Email/EmailStringCollection.cs b/ICD.Common.Utils/Email/EmailStringCollection.cs new file mode 100644 index 0000000..981ed38 --- /dev/null +++ b/ICD.Common.Utils/Email/EmailStringCollection.cs @@ -0,0 +1,134 @@ +using System.Collections; +using System.Collections.Generic; +using ICD.Common.Properties; +using ICD.Common.Utils.Extensions; + +namespace ICD.Common.Utils.Email +{ + /// + /// Stores addresses/paths and provides them in a ; delimited string. + /// + public sealed class EmailStringCollection : ICollection + { + private const char DELIMITER = ';'; + + private readonly List m_Items; + + /// + /// Gets the number of items. + /// + public int Count { get { return m_Items.Count; } } + + public bool IsReadOnly { get { return false; } } + + /// + /// Constructor. + /// + public EmailStringCollection() + { + m_Items = new List(); + } + + #region Methods + + /// + /// Adds the item to the collection. + /// + /// + /// + [PublicAPI] + public bool Add(string item) + { + if (m_Items.Contains(item)) + return false; + + m_Items.Add(item); + return true; + } + + /// + /// Removes the item from the collection. + /// + /// + /// + [PublicAPI] + public bool Remove(string item) + { + if (!m_Items.Contains(item)) + return false; + + m_Items.Remove(item); + return true; + } + + /// + /// Clears all items. + /// + public void Clear() + { + m_Items.Clear(); + } + + /// + /// Returns true if the collection contains the item. + /// + /// + /// + [PublicAPI] + public bool Contains(string item) + { + return m_Items.Contains(item); + } + + /// + /// Gets the items as a ; delimited string. + /// + /// + public override string ToString() + { + return string.Join(DELIMITER.ToString(), m_Items.ToArray()); + } + + #endregion + + #region Collection + + void ICollection.Add(string item) + { + Add(item); + } + + /// + /// Adds multiple email addresses, seperated by ; + /// + /// + public void AddMany(string items) + { + items = items.RemoveWhitespace(); + string [] splitItems = items.Split(DELIMITER); + foreach (string item in splitItems) + Add(item); + } + + public void CopyTo(string[] array, int arrayIndex) + { + m_Items.CopyTo(array, arrayIndex); + } + + #endregion + + #region Enumerable + + public IEnumerator GetEnumerator() + { + return m_Items.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} \ No newline at end of file diff --git a/ICD.Common.Utils/Email/eMailErrorCode.cs b/ICD.Common.Utils/Email/eMailErrorCode.cs new file mode 100644 index 0000000..1aa6628 --- /dev/null +++ b/ICD.Common.Utils/Email/eMailErrorCode.cs @@ -0,0 +1,233 @@ +using System; +#if STANDARD +using System.Net.Mail; +#endif +#if SIMPLSHARP +using Crestron.SimplSharp; +#endif + +namespace ICD.Common.Utils.Email +{ + public enum eMailErrorCode + { + // + // Summary: + // The transaction could not occur. You receive this error when the specified SMTP + // host cannot be found. + GeneralFailure = -1, + // + // Summary: + // A system status or system Help reply. + SystemStatus = 211, + // + // Summary: + // A Help message was returned by the service. + HelpMessage = 214, + // + // Summary: + // The SMTP service is ready. + ServiceReady = 220, + // + // Summary: + // The SMTP service is closing the transmission channel. + ServiceClosingTransmissionChannel = 221, + // + // Summary: + // The email was successfully sent to the SMTP service. + Ok = 250, + // + // Summary: + // The user mailbox is not located on the receiving server; the server forwards + // the e-mail. + UserNotLocalWillForward = 251, + // + // Summary: + // The specified user is not local, but the receiving SMTP service accepted the + // message and attempted to deliver it. This status code is defined in RFC 1123, + // which is available at http://www.ietf.org/. + CannotVerifyUserWillAttemptDelivery = 252, + // + // Summary: + // The SMTP service is ready to receive the e-mail content. + StartMailInput = 354, + // + // Summary: + // The SMTP service is not available; the server is closing the transmission channel. + ServiceNotAvailable = 421, + // + // Summary: + // The destination mailbox is in use. + MailboxBusy = 450, + // + // Summary: + // The SMTP service cannot complete the request. This error can occur if the client's + // IP address cannot be resolved (that is, a reverse lookup failed). You can also + // receive this error if the client domain has been identified as an open relay + // or source for unsolicited e-mail (spam). For details, see RFC 2505, which is + // available at http://www.ietf.org/. + LocalErrorInProcessing = 451, + // + // Summary: + // The SMTP service does not have sufficient storage to complete the request. + InsufficientStorage = 452, + // + // Summary: + // The client was not authenticated or is not allowed to send mail using the specified + // SMTP host. + ClientNotPermitted = 454, + // + // Summary: + // The SMTP service does not recognize the specified command. + CommandUnrecognized = 500, + // + // Summary: + // The syntax used to specify a command or parameter is incorrect. + SyntaxError = 501, + // + // Summary: + // The SMTP service does not implement the specified command. + CommandNotImplemented = 502, + // + // Summary: + // The commands were sent in the incorrect sequence. + BadCommandSequence = 503, + // + // Summary: + // The SMTP service does not implement the specified command parameter. + CommandParameterNotImplemented = 504, + // + // Summary: + // The SMTP server is configured to accept only TLS connections, and the SMTP client + // is attempting to connect by using a non-TLS connection. The solution is for the + // user to set EnableSsl=true on the SMTP Client. + MustIssueStartTlsFirst = 530, + // + // Summary: + // The destination mailbox was not found or could not be accessed. + MailboxUnavailable = 550, + // + // Summary: + // The user mailbox is not located on the receiving server. You should resend using + // the supplied address information. + UserNotLocalTryAlternatePath = 551, + // + // Summary: + // The message is too large to be stored in the destination mailbox. + ExceededStorageAllocation = 552, + // + // Summary: + // The syntax used to specify the destination mailbox is incorrect. + MailboxNameNotAllowed = 553, + // + // Summary: + // The transaction failed. + TransactionFailed = 554 + } + + public static class MailErrorCodeUtils + { +#if STANDARD + public static eMailErrorCode FromNetStandardMailCode(SmtpStatusCode code) + { + switch (code) + { + case SmtpStatusCode.BadCommandSequence: + return eMailErrorCode.BadCommandSequence; + case SmtpStatusCode.CannotVerifyUserWillAttemptDelivery: + return eMailErrorCode.CannotVerifyUserWillAttemptDelivery; + case SmtpStatusCode.ClientNotPermitted: + return eMailErrorCode.ClientNotPermitted; + case SmtpStatusCode.CommandNotImplemented: + return eMailErrorCode.CommandNotImplemented; + case SmtpStatusCode.CommandParameterNotImplemented: + return eMailErrorCode.CommandParameterNotImplemented; + case SmtpStatusCode.CommandUnrecognized: + return eMailErrorCode.CommandUnrecognized; + case SmtpStatusCode.ExceededStorageAllocation: + return eMailErrorCode.ExceededStorageAllocation; + case SmtpStatusCode.GeneralFailure: + return eMailErrorCode.GeneralFailure; + case SmtpStatusCode.HelpMessage: + return eMailErrorCode.HelpMessage; + case SmtpStatusCode.InsufficientStorage: + return eMailErrorCode.InsufficientStorage; + case SmtpStatusCode.LocalErrorInProcessing: + return eMailErrorCode.LocalErrorInProcessing; + case SmtpStatusCode.MailboxBusy: + return eMailErrorCode.MailboxBusy; + case SmtpStatusCode.MailboxNameNotAllowed: + return eMailErrorCode.MailboxNameNotAllowed; + case SmtpStatusCode.MailboxUnavailable: + return eMailErrorCode.MailboxUnavailable; + case SmtpStatusCode.MustIssueStartTlsFirst: + return eMailErrorCode.MustIssueStartTlsFirst; + case SmtpStatusCode.Ok: + return eMailErrorCode.Ok; + case SmtpStatusCode.ServiceClosingTransmissionChannel: + return eMailErrorCode.ServiceClosingTransmissionChannel; + case SmtpStatusCode.ServiceNotAvailable: + return eMailErrorCode.ServiceNotAvailable; + case SmtpStatusCode.ServiceReady: + return eMailErrorCode.ServiceReady; + case SmtpStatusCode.StartMailInput: + return eMailErrorCode.StartMailInput; + case SmtpStatusCode.SyntaxError: + return eMailErrorCode.SyntaxError; + case SmtpStatusCode.SystemStatus: + return eMailErrorCode.SystemStatus; + case SmtpStatusCode.TransactionFailed: + return eMailErrorCode.TransactionFailed; + case SmtpStatusCode.UserNotLocalTryAlternatePath: + return eMailErrorCode.UserNotLocalTryAlternatePath; + case SmtpStatusCode.UserNotLocalWillForward: + return eMailErrorCode.UserNotLocalWillForward; + default: + throw new ArgumentOutOfRangeException(nameof(code), code, null); + } + } +#endif +#if SIMPLSHARP + public static eMailErrorCode FromSimplMailCode(CrestronMailFunctions.SendMailErrorCodes code) + { + switch (code) + { + case CrestronMailFunctions.SendMailErrorCodes.SMTP_OK: + return eMailErrorCode.Ok; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_FATAL: + return eMailErrorCode.GeneralFailure; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_ILLEGAL_CMD: + return eMailErrorCode.CommandUnrecognized; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_CONNECT: + return eMailErrorCode.ServiceNotAvailable; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_SEND: + return eMailErrorCode.GeneralFailure; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_RECV: + return eMailErrorCode.GeneralFailure; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_NU_CONNECT: + return eMailErrorCode.GeneralFailure; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_NU_BUFFERS: + return eMailErrorCode.GeneralFailure; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_AUTHENTICATION: + return eMailErrorCode.GeneralFailure; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ERROR_AUTH_LOGIN_UNSUPPORTED: + return eMailErrorCode.MustIssueStartTlsFirst; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_INV_PARAM: + return eMailErrorCode.SyntaxError; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_ETHER_NOT_ENABLED: + return eMailErrorCode.ServiceNotAvailable; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_NO_SERVER_ADDRESS: + return eMailErrorCode.ServiceNotAvailable; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_SEND_FAILED: + return eMailErrorCode.GeneralFailure; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_GENERAL_SENDMAIL_ERROR: + return eMailErrorCode.GeneralFailure; + case CrestronMailFunctions.SendMailErrorCodes.SMTP_INVALID_FIRMWARE: + return eMailErrorCode.GeneralFailure; + default: + throw new ArgumentOutOfRangeException("code"); + } + } +#endif + } +} + \ No newline at end of file diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index ba191bc..8b871ff 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -84,6 +84,9 @@ + + +