/// <summary> /// This method handle successful delivery. /// Logs success /// Deletes queued data /// </summary> public async Task <bool> HandleDeliverySuccessAsync(VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord, string response) { await MtaTransaction.LogTransactionAsync(this, TransactionStatus.Success, response, ipAddress, mxRecord); IsHandled = true; return(true); }
/// <summary> /// /// </summary> /// <param name="ipAddress"></param> /// <param name="mxRecord"></param> /// <returns></returns> public async Task <bool> HandleFailedToConnectAsync(VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord) { // If there was no MX record in DNS, so using A, we should fail and not retry. if (mxRecord.MxRecordSrc == DNS.MxRecordSrc.A) { return(await HandleDeliveryFailAsync("550 Failed to connect", ipAddress, mxRecord)); } else { return(await HandleDeliveryDeferralAsync("Failed to connect", ipAddress, mxRecord, false, 15)); } }
/// <summary> /// Logs an MTA Transaction to the database. /// </summary> public static async Task <bool> LogTransactionAsync(MtaMessage msg, TransactionStatus status, string svrResponse, VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord) { using (SqlConnection conn = MantaDB.GetSqlConnection()) { SqlCommand cmd = conn.CreateCommand(); cmd.CommandText = @" BEGIN TRANSACTION INSERT INTO man_mta_transaction (mta_msg_id, ip_ipAddress_id, mta_transaction_timestamp, mta_transactionStatus_id, mta_transaction_serverResponse, mta_transaction_serverHostname) VALUES(@msgID, @ipAddressID, GETUTCDATE(), @status, @serverResponse, @serverHostname)"; switch (status) { case TransactionStatus.Discarded: case TransactionStatus.Failed: case TransactionStatus.TimedOut: cmd.CommandText += @"UPDATE man_mta_send SET mta_send_rejected = mta_send_rejected + 1 WHERE mta_send_internalID = @sendInternalID" ; break; case TransactionStatus.Success: cmd.CommandText += @"UPDATE man_mta_send SET mta_send_accepted = mta_send_accepted + 1 WHERE mta_send_internalID = @sendInternalID" ; break; } cmd.CommandText += " COMMIT TRANSACTION"; cmd.Parameters.AddWithValue("@sendInternalID", msg.InternalSendID); cmd.Parameters.AddWithValue("@msgID", msg.ID); if (ipAddress != null) { cmd.Parameters.AddWithValue("@ipAddressID", ipAddress.ID); } else { cmd.Parameters.AddWithValue("@ipAddressID", DBNull.Value); } if (mxRecord != null) { cmd.Parameters.AddWithValue("@serverHostname", mxRecord.Host); } else { cmd.Parameters.AddWithValue("@serverHostname", DBNull.Value); } cmd.Parameters.AddWithValue("@status", (int)status); cmd.Parameters.AddWithValue("@serverResponse", svrResponse); await conn.OpenAsync(); await cmd.ExecuteNonQueryAsync(); return(true); } }
/// <summary> /// This method handles message deferal. /// Logs deferral /// Fails the message if timed out /// or /// Sets the next rety date time /// </summary> /// <param name="defMsg">The deferal message from the SMTP server.</param> /// <param name="ipAddress">IP Address that send was attempted from.</param> /// <param name="mxRecord">MX Record of the server tried to send too.</param> /// <param name="isServiceUnavailable">If false will backoff the retry, if true will use the MtaParameters.MtaRetryInterval, /// this is needed to reduce the tail when sending as a message could get multiple try again laters and soon be 1h+ before next retry.</param> public async Task <bool> HandleDeliveryDeferralAsync(string defMsg, VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord, bool isServiceUnavailable = false, int?overrideTimeminutes = null) { // Log the deferral. await MtaTransaction.LogTransactionAsync(this, TransactionStatus.Deferred, defMsg, ipAddress, mxRecord); // This holds the maximum interval between send retries. Should be put in the database. int maxInterval = 3 * 60; // Increase the defered count as the queued messages has been deferred. DeferredCount++; // Hold the minutes to wait until next retry. double nextRetryInterval = MtaParameters.MtaRetryInterval; if (overrideTimeminutes.HasValue) { nextRetryInterval = overrideTimeminutes.Value; } else { if (!isServiceUnavailable) { // Increase the deferred wait interval by doubling for each retry. for (int i = 1; i < DeferredCount; i++) { nextRetryInterval = nextRetryInterval * 2; } // If we have gone over the max interval then set to the max interval value. if (nextRetryInterval > maxInterval) { nextRetryInterval = maxInterval; } } else { nextRetryInterval = 1; // For service unavalible use 1 minute between retries. } } // Set next retry time and release the lock. this.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(nextRetryInterval); Requeue(); return(true); }
/// <summary> /// Logs an MTA Transaction to the database. /// </summary> public static void LogTransaction(MtaMessage msg, TransactionStatus status, string svrResponse, VirtualMta.VirtualMTA ipAddress, DNS.MXRecord mxRecord) { LogTransactionAsync(msg, status, svrResponse, ipAddress, mxRecord).Wait(); }