/// <summary> /// Creates a MantaEvent object and Fills it with the values from the data record. /// </summary> /// <param name="record">Record to get the data from.</param> /// <returns>MantaAubseEvent or MantaBounceEvent</returns> private static MantaEvent CreateAndFillMantaEventFromRecord(IDataRecord record) { MantaEventType type = (MantaEventType)record.GetInt32("evn_type_id"); MantaEvent thisEvent = null; switch (type) { case MantaEventType.Abuse: thisEvent = new MantaAbuseEvent(); break; case MantaEventType.Bounce: thisEvent = new MantaBounceEvent(); FillMantaBounceEvent((thisEvent as MantaBounceEvent), record); break; case MantaEventType.TimedOutInQueue: thisEvent = new MantaTimedOutInQueueEvent(); break; default: throw new NotImplementedException("Unknown Event Type (" + type + ")"); } thisEvent.EmailAddress = record.GetString("evn_event_emailAddress"); thisEvent.EventTime = record.GetDateTime("evn_event_timestamp"); thisEvent.EventType = type; thisEvent.ID = record.GetInt32("evn_event_id"); thisEvent.SendID = record.GetString("snd_send_id"); thisEvent.Forwarded = record.GetBoolean("evn_event_forwarded"); return(thisEvent); }
public void SaveAndGetBounce() { using (CreateTransactionScopeObject()) { MantaBounceEvent originalEvt = new MantaBounceEvent { BounceInfo = new BouncePair { BounceCode = MantaBounceCode.BadEmailAddress, BounceType = MantaBounceType.Hard }, EmailAddress = "*****@*****.**", EventTime = DateTime.UtcNow, EventType = MantaEventType.Bounce, Message = "550 Invalid Inbox", SendID = "qwerty" }; originalEvt.ID = EventsManager.Instance.Save(originalEvt); MantaBounceEvent savedEvt = (MantaBounceEvent)EventsManager.Instance.GetEvent(originalEvt.ID); Assert.NotNull(savedEvt); Assert.AreEqual(originalEvt.BounceInfo.BounceCode, savedEvt.BounceInfo.BounceCode); Assert.AreEqual(originalEvt.BounceInfo.BounceType, savedEvt.BounceInfo.BounceType); Assert.AreEqual(originalEvt.EmailAddress, savedEvt.EmailAddress); Assert.That(savedEvt.EventTime, Is.EqualTo(originalEvt.EventTime).Within(TimeSpan.FromSeconds(1))); Assert.AreEqual(originalEvt.EventType, savedEvt.EventType); Assert.AreEqual(originalEvt.ID, savedEvt.ID); Assert.AreEqual(originalEvt.Message, savedEvt.Message); Assert.AreEqual(originalEvt.SendID, savedEvt.SendID); } }
public void NonDeliveryReport() { using (CreateTransactionScopeObject()) { string emailContent = System.IO.File.OpenText(@".\..\..\NonDeliveryReport Test Email.eml").ReadToEnd(); EmailProcessingDetails processingDetail = EventsManager.Instance.ProcessBounceEmail(emailContent); Assert.AreEqual(EmailProcessingResult.SuccessBounce, processingDetail.ProcessingResult); MantaEventCollection events = EventsManager.Instance.GetEvents(); Assert.AreEqual(1, events.Count); Assert.IsTrue(events[0] is MantaBounceEvent); MantaBounceEvent bounce = (MantaBounceEvent)events[0]; Assert.AreEqual(MantaBounceCode.BadEmailAddress, bounce.BounceInfo.BounceCode); Assert.AreEqual(MantaBounceType.Hard, bounce.BounceInfo.BounceType); Assert.AreEqual("*****@*****.**", bounce.EmailAddress); Assert.AreEqual("550 5.1.1 unknown or illegal alias: [email protected]", bounce.Message); Assert.AreEqual("TestData", bounce.SendID); } }
public void SmtpResponseTest() { using (CreateTransactionScopeObject()) { EmailProcessingDetails processingDetails; bool result = EventsManager.Instance.ProcessSmtpResponseMessage("550 User Unknown", "*****@*****.**", 1, out processingDetails); Assert.IsTrue(result); MantaEventCollection events = EventsManager.Instance.GetEvents(); Assert.AreEqual(1, events.Count); Assert.IsTrue(events[0] is MantaBounceEvent); MantaBounceEvent bounce = (MantaBounceEvent)events[0]; Assert.AreEqual(MantaBounceCode.BadEmailAddress, bounce.BounceInfo.BounceCode); Assert.AreEqual(MantaBounceType.Hard, bounce.BounceInfo.BounceType); Assert.AreEqual("*****@*****.**", bounce.EmailAddress); Assert.AreEqual("550 User Unknown", bounce.Message); Assert.AreEqual("TestData", bounce.SendID); } }
/// <summary> /// Fills the MantaBounceEvent with values from <paramref name="record"/> /// </summary> /// <param name="evt">The MantaBounceEvent to fill.</param> /// <param name="record">The data record to fill with.</param> private static void FillMantaBounceEvent(MantaBounceEvent evt, IDataRecord record) { if (record.IsDBNull("evn_bounceCode_id")) // The bounce record is incomplete { evt.BounceInfo = new BouncePair { BounceCode = MantaBounceCode.Unknown, // Don't know what the bounce was. BounceType = MantaBounceType.Soft // Assume soft bounce, just to be nice. If it happens 3 times sentoi will mark bad. }; evt.Message = string.Empty; // There is no message. } else { evt.BounceInfo = new BouncePair { BounceCode = (MantaBounceCode)record.GetInt32("evn_bounceCode_id"), BounceType = (MantaBounceType)record.GetInt32("evn_bounceType_id") }; evt.Message = record.GetString("evn_bounceEvent_message"); } }
/// <summary> /// Examines an email to try to identify detailed bounce information from it. /// </summary> /// <param name="filename">Path and filename of the file being processed.</param> /// <param name="message">The entire text content for an email (headers and body).</param> /// <returns>An EmailProcessingResult value indicating whether the the email was /// successfully processed or not.</returns> public EmailProcessingDetails ProcessBounceEmail(string message) { EmailProcessingDetails bounceDetails = new EmailProcessingDetails(); MimeMessage msg = MimeMessage.Parse(message); if (msg == null) { bounceDetails.ProcessingResult = EmailProcessingResult.ErrorContent; bounceDetails.BounceIdentifier = BounceIdentifier.NotIdentifiedAsABounce; return(bounceDetails); } // "X-Recipient" should contain what Manta originally set as the "return-path" when sending. MessageHeader returnPath = msg.Headers.GetFirstOrDefault("X-Recipient"); if (returnPath == null) { bounceDetails.ProcessingResult = EmailProcessingResult.ErrorNoReturnPath; bounceDetails.BounceIdentifier = BounceIdentifier.UnknownReturnPath; return(bounceDetails); } string rcptTo = string.Empty; int internalSendID = 0; if (!ReturnPathManager.TryDecode(returnPath.Value, out rcptTo, out internalSendID)) { // Not a valid Return-Path so can't process. bounceDetails.ProcessingResult = EmailProcessingResult.ErrorNoReturnPath; bounceDetails.BounceIdentifier = BounceIdentifier.UnknownReturnPath; return(bounceDetails); } MantaBounceEvent bounceEvent = new MantaBounceEvent(); bounceEvent.EmailAddress = rcptTo; bounceEvent.SendID = SendDB.GetSend(internalSendID).ID; // TODO: Might be good to get the DateTime found in the email. bounceEvent.EventTime = DateTime.UtcNow; // These properties are both set according to the SMTP code we find, if any. bounceEvent.BounceInfo.BounceCode = MantaBounceCode.Unknown; bounceEvent.BounceInfo.BounceType = MantaBounceType.Unknown; bounceEvent.EventType = MantaEventType.Bounce; // First, try to find a NonDeliveryReport body part as that's the proper way for an MTA // to tell us there was an issue sending the email. BouncePair bouncePair; string bounceMsg; BodyPart deliveryReportBodyPart; string deliveryReport = string.Empty; if (FindFirstBodyPartByMediaType(msg.BodyParts, "message/delivery-status", out deliveryReportBodyPart)) { // If we've got a delivery report, check it for info. // Abuse report content may have long lines whitespace folded. deliveryReport = MimeMessage.UnfoldHeaders(deliveryReportBodyPart.GetDecodedBody()); if (ParseNdr(deliveryReport, out bouncePair, out bounceMsg, out bounceDetails)) { // Successfully parsed. bounceEvent.BounceInfo = bouncePair; bounceEvent.Message = bounceMsg; // Write BounceEvent to DB. Save(bounceEvent); bounceDetails.ProcessingResult = EmailProcessingResult.SuccessBounce; return(bounceDetails); } } // We're still here so there was either no NDR part or nothing contained within it that we could // interpret so have to check _all_ body parts for something useful. if (FindBounceReason(msg.BodyParts, out bouncePair, out bounceMsg, out bounceDetails)) { bounceEvent.BounceInfo = bouncePair; bounceEvent.Message = bounceMsg; // Write BounceEvent to DB. Save(bounceEvent); bounceDetails.ProcessingResult = EmailProcessingResult.SuccessBounce; return(bounceDetails); } // Nope - no clues relating to why the bounce occurred. bounceEvent.BounceInfo.BounceType = MantaBounceType.Unknown; bounceEvent.BounceInfo.BounceCode = MantaBounceCode.Unknown; bounceEvent.Message = string.Empty; bounceDetails.BounceIdentifier = BounceIdentifier.NotIdentifiedAsABounce; bounceDetails.ProcessingResult = EmailProcessingResult.Unknown; return(bounceDetails); }
/// <summary> /// Examines an SMTP response message to identify detailed bounce information from it. /// </summary> /// <param name="response">The message that's come back from an external MTA when attempting to send an email.</param> /// <param name="rcptTo">The email address that was being sent to.</param> /// <param name="internalSendID">The internal Manta SendID.</param> /// <returns>True if a bounce was found and recorded, false if not.</returns> internal bool ProcessSmtpResponseMessage(string response, string rcptTo, int internalSendID, out EmailProcessingDetails bounceIdentification) { bounceIdentification = new EmailProcessingDetails(); // Check for TimedOutInQueue message first. if (response.Equals(MtaParameters.TIMED_OUT_IN_QUEUE_MESSAGE, StringComparison.OrdinalIgnoreCase)) { bounceIdentification = null; MantaTimedOutInQueueEvent timeOut = new MantaTimedOutInQueueEvent { EventType = MantaEventType.TimedOutInQueue, EmailAddress = rcptTo, SendID = SendDB.GetSend(internalSendID).ID, EventTime = DateTime.UtcNow }; // Log to DB. Save(timeOut); // All done return true. return(true); } BouncePair bouncePair = new BouncePair(); string bounceMessage = string.Empty; if (ParseBounceMessage(response, out bouncePair, out bounceMessage, out bounceIdentification)) { // Were able to find the bounce so create the bounce event. MantaBounceEvent bounceEvent = new MantaBounceEvent { EventType = MantaEventType.Bounce, EmailAddress = rcptTo, BounceInfo = bouncePair, SendID = SendDB.GetSend(internalSendID).ID, // It is possible that the bounce was generated a while back, but we're assuming "now" for the moment. // Might be good to get the DateTime found in the email at a later point. EventTime = DateTime.UtcNow, Message = response }; // Log to DB. Save(bounceEvent); // All done return true. return(true); } // Couldn't identify the bounce. bounceIdentification.BounceIdentifier = BounceIdentifier.NotIdentifiedAsABounce; return(false); }