/// <summary> /// Send an email message /// </summary> /// <param name="AEmail">on successful sending, the header is modified with the sent date</param> /// <returns>true if email was sent successfully</returns> public bool SendMessage(MailMessage AEmail) { if (AEmail.Headers.Get("Date-Sent") != null) { // don't send emails several times return false; } FailedRecipients.Clear(); if (FSmtpClient.Host.EndsWith("example.org")) { TLogging.Log("Not sending the email, since the configuration is just with an example server: " + FSmtpClient.Host); TLogging.Log("You can configure the mail settings in the config file."); return false; } //Attempt to send the email try { AEmail.IsBodyHtml = AEmail.Body.ToLower().Contains("<html>"); int AttemptCount = 3; while (AttemptCount > 0) { AttemptCount--; try { // for office365, this takes about 15 seconds FSmtpClient.Send(AEmail); AEmail.Headers.Add("Date-Sent", DateTime.Now.ToString()); return true; } catch (Exception e) { if (AttemptCount > 0) { Thread.Sleep(TimeSpan.FromMinutes(1)); } else { throw e; } } } } catch (SmtpFailedRecipientsException frEx) // If the SMTP server knows that the send failed because of failed recipients, { // I can produce a list of failed recipient addresses, and return false. // The caller can then retrieve the list and inform the user. TLogging.Log("SmtpEmail: Email to the following addresses did not succeed:"); SmtpFailedRecipientException[] failureList = frEx.InnerExceptions; foreach (SmtpFailedRecipientException problem in failureList) { TsmtpFailedRecipient FailureDetails = new TsmtpFailedRecipient(); FailureDetails.FailedAddress = problem.FailedRecipient; FailureDetails.FailedMessage = problem.Message; FailedRecipients.Add(FailureDetails); TLogging.Log(problem.FailedRecipient + " : " + problem.Message); } return false; } catch (Exception ex) { // SSL authentication error: RemoteCertificateNotAvailable // see http://mono.1490590.n4.nabble.com/SSL-authentication-error-RemoteCertificateNotAvailable-RemoteCertificateChainErrors-td1755733.html // and http://www.mono-project.com/FAQ:_Security#Does_SSL_works_for_SMTP.2C_like_GMail_.3F // on Mono command prompt: // mozroots --import --ask-remove --machine // mozroots --import --ask-remove // certmgr -ssl smtps://tim00.hostsharing.net:443 TLogging.Log("There has been a problem sending the email"); TLogging.Log("server: " + FSmtpClient.Host + ":" + FSmtpClient.Port.ToString()); TLogging.Log(ex.ToString() + " " + ex.Message); TLogging.Log(ex.StackTrace); throw; } return false; }
/// <summary> /// Send an email message /// </summary> /// <param name="AEmail">on successful sending, the header is modified with the sent date</param> /// <returns>true if email was sent successfully</returns> public bool SendMessage(MailMessage AEmail) { if (AEmail.Headers.Get("Date-Sent") != null) { // don't send emails several times return(false); } FailedRecipients.Clear(); if (FSmtpClient.Host.EndsWith("example.org")) { TLogging.Log("Not sending the email, since the configuration is just with an example server: " + FSmtpClient.Host); TLogging.Log("You can configure the mail settings in the config file."); return(false); } //Attempt to send the email try { AEmail.IsBodyHtml = AEmail.Body.ToLower().Contains("<html>"); int AttemptCount = 3; while (AttemptCount > 0) { AttemptCount--; try { // for office365, this takes about 15 seconds FSmtpClient.Send(AEmail); AEmail.Headers.Add("Date-Sent", DateTime.Now.ToString()); return(true); } catch (Exception e) { if (AttemptCount > 0) { Thread.Sleep(TimeSpan.FromMinutes(1)); } else { throw e; } } } } catch (SmtpFailedRecipientsException frEx) // If the SMTP server knows that the send failed because of failed recipients, { // I can produce a list of failed recipient addresses, and return false. // The caller can then retrieve the list and inform the user. TLogging.Log("SmtpEmail: Email to the following addresses did not succeed:"); SmtpFailedRecipientException[] failureList = frEx.InnerExceptions; foreach (SmtpFailedRecipientException problem in failureList) { TsmtpFailedRecipient FailureDetails = new TsmtpFailedRecipient(); FailureDetails.FailedAddress = problem.FailedRecipient; FailureDetails.FailedMessage = problem.Message; FailedRecipients.Add(FailureDetails); TLogging.Log(problem.FailedRecipient + " : " + problem.Message); } return(false); } catch (Exception ex) { // SSL authentication error: RemoteCertificateNotAvailable // see http://mono.1490590.n4.nabble.com/SSL-authentication-error-RemoteCertificateNotAvailable-RemoteCertificateChainErrors-td1755733.html // and http://www.mono-project.com/FAQ:_Security#Does_SSL_works_for_SMTP.2C_like_GMail_.3F // on Mono command prompt: // mozroots --import --ask-remove --machine // mozroots --import --ask-remove // certmgr -ssl smtps://tim00.hostsharing.net:443 TLogging.Log("There has been a problem sending the email"); TLogging.Log("server: " + FSmtpClient.Host + ":" + FSmtpClient.Port.ToString()); TLogging.Log(ex.ToString() + " " + ex.Message); TLogging.Log(ex.StackTrace); throw; } return(false); }
/// <summary> /// Send an email message /// </summary> /// <param name="AEmail">On successful sending, the header is modified with the sent date.</param> /// <returns>True if email was sent successfully.</returns> public bool SendMessage(MimeMessage AEmail) { if (AEmail.Headers["Date-Sent"] != null) { // don't send emails several times return(false); } FFailedRecipients.Clear(); //Attempt to send the email // FIXME!! Some SMTP servers have a message rate limit, so if a message is rejected because we have exceeded the maximum number // of messages per minute, we need to wait and retry (https://tracker.openpetra.org/view.php?id=3179). Unfortunately the solution // here doesn't check what _kind_ of error occurred. So for a permanent error like incorrect credentials... for 100 HOSAs... it // will happily retry one after another for 5 hours before telling the user they all failed. // // You can catch the SmtpClient exceptions and check the StatusCode enum to get a better idea of the problem. The numeric values of // the enum match SMTP error codes as shown below. Codes in the 400 range are generally 'temporary' failures while codes in the 500 // range are usually'permanent'. It's more complicated than it should be because: // i) If a MimeMessage has one To: address, failure is returned in a SmtpFailedRecipientException. // If a MimeMessage has one To: address and one CC: address, failure is returned in a SmtpFailedRecipientsException (note the plural). // This contains an InnerExceptions property containing the individual SmtpFailedRecipientExceptions which Microsoft say is "not // intended to be used directly from your code". // ii) There are different kinds of "permanent" errors and SMTP servers aren't consistent about how they report things. For example, // for an authentication error, mail.smtp2go.com returns "Mailbox Unavailable. The server response was: Relay denied for unauthenticated sender". // You need to check the exception Message text to see whether the error is to do with the recipient mailbox (so mail to a // different recipient may work) or the sender account (so nothing will work and you may as well abort the whole process now). // // SmtpStatusCode Enumeration with numeric values: // -1 GeneralFailure // 211 SystemStatus // 214 HelpMessage // 220 ServiceReady // 221 ServiceClosingTransmissionChannel // 250 Ok // 251 UserNotLocalWillForward // 252 CannotVerifyUserWillAttemptDelivery // 354 StartMailInput // 421 ServiceNotAvailable // 450 MailboxBusy // 451 LocalErrorInProcessing // 452 InsufficientStorage // 454 ClientNotPermitted // 500 CommandUnrecognized // 501 SyntaxError // 502 CommandNotImplemented // 503 BadCommandSequence // 504 CommandParameterNotImplemented // 530 MustIssueStartTlsFirst // 550 MailboxUnavailable // 551 UserNotLocalTryAlternatePath // 552 ExceededStorageAllocation // 553 MailboxNameNotAllowed // 554 TransactionFailed try { int AttemptCount = 3; while (AttemptCount > 0) { AttemptCount--; try { TLogging.LogAtLevel(1, "Trying to send E-Mail to " + AEmail.To.ToString() + " from " + AEmail.From.ToString()); if (!AEmail.From.ToString().Substring(AEmail.From.ToString().IndexOf("@")).Contains(".")) { // invalid Email domain, eg. local return(false); } // for office365, this takes about 15 seconds FSmtpClient.Send(AEmail); AEmail.Headers.Add("Date-Sent", DateTime.Now.ToString()); TLogging.LogAtLevel(1, "E-Mail was sent successfully"); return(true); } catch (Exception ex) { if (AttemptCount > 0) { Thread.Sleep(TimeSpan.FromMinutes(1)); } else { TLogging.LogException(ex, Utilities.GetMethodSignature()); throw; } } } } catch (SmtpCommandException frEx) // If the SMTP server knows that the send failed because of failed recipients, { // I can produce a list of failed recipient addresses, and return false. // The caller can then retrieve the list and inform the user. TLogging.Log("SmtpEmail: Email to the following addresses did not succeed:"); if (frEx.ErrorCode == SmtpErrorCode.RecipientNotAccepted) { TsmtpFailedRecipient FailureDetails = new TsmtpFailedRecipient(); FailureDetails.FailedAddress = frEx.Mailbox.ToString(); FailureDetails.FailedMessage = "Recipient not accepted"; FFailedRecipients.Add(FailureDetails); TLogging.Log(FailureDetails.FailedAddress + " : " + FailureDetails.FailedMessage); } return(false); } catch (Exception ex) { // SSL authentication error: RemoteCertificateNotAvailable // see http://mono.1490590.n4.nabble.com/SSL-authentication-error-RemoteCertificateNotAvailable-RemoteCertificateChainErrors-td1755733.html // and http://www.mono-project.com/FAQ:_Security#Does_SSL_works_for_SMTP.2C_like_GMail_.3F // on Mono command prompt: // mozroots --import --ask-remove --machine // mozroots --import --ask-remove // certmgr -ssl smtps://tim00.hostsharing.net:443 TLogging.Log("There has been a problem sending the email"); TLogging.Log(ex.ToString() + " " + ex.Message); TLogging.Log(ex.StackTrace); throw; } return(false); }