コード例 #1
0
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";

            string[] relayingIPs = MtaParameters.IPsToAllowRelaying;
            if (!relayingIPs.Contains(context.Request.UserHostAddress))
            {
                context.Response.Write("Forbidden");
                return;
            }


            string sendID = context.Request.QueryString["SendID"];
            string name   = context.Request.QueryString["Name"];
            string value  = context.Request.QueryString["Value"];

            if (string.IsNullOrWhiteSpace(sendID) || string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(value))
            {
                context.Response.Write("bad");
                return;
            }

            Send snd = SendManager.Instance.GetSend(sendID);

            SendDB.SaveSendMetadata(snd.InternalID, new SendMetadata {
                Name  = name,
                Value = value
            });


            context.Response.Write("ok");
        }
コード例 #2
0
ファイル: SendsController.cs プロジェクト: simongh/MantaMTA
        //
        // GET: /Sends/
        public ActionResult Index(int page = 1, int pageSize = 10)
        {
            SendInfoCollection sends = SendDB.GetSends(pageSize, page);
            int pages = (int)Math.Ceiling(SendDB.GetSendsCount() / Convert.ToDouble(pageSize));

            return(View(new SendsModel(sends, page, pages)));
        }
コード例 #3
0
        //
        // GET: /Dashboard/
        public ActionResult Index()
        {
            DashboardModel model = new DashboardModel
            {
                SendTransactionSummaryCollection = TransactionDB.GetLastHourTransactionSummary(),
                Waiting       = SendDB.GetQueueCount(new SendStatus[] { SendStatus.Active, SendStatus.Discard }),
                Paused        = SendDB.GetQueueCount(new SendStatus[] { SendStatus.Paused }),
                BounceInfo    = TransactionDB.GetLastHourBounceInfo(3),
                SendSpeedInfo = TransactionDB.GetLastHourSendSpeedInfo()
            };

            try
            {
                // Connect to Rabbit MQ and grab basic queue counts.
                HttpWebRequest request = HttpWebRequest.CreateHttp("http://localhost:15672/api/queues");
                request.Credentials = new NetworkCredential(MtaParameters.RabbitMQ.Username, MtaParameters.RabbitMQ.Password);
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    string json         = new StreamReader(response.GetResponseStream()).ReadToEnd();
                    JArray rabbitQueues = JArray.Parse(json);
                    foreach (JToken q in rabbitQueues.Children())
                    {
                        JEnumerable <JProperty> qProperties = q.Children <JProperty>();
                        string queueName = (string)qProperties.First(x => x.Name.Equals("name")).Value;
                        if (queueName.StartsWith("manta_mta_"))
                        {
                            long messages = (long)qProperties.First(x => x.Name.Equals("messages", System.StringComparison.OrdinalIgnoreCase)).Value;
                            if (queueName.IndexOf("_inbound") > 0)
                            {
                                model.RabbitMqInbound += messages;
                            }
                            else if (queueName.IndexOf("_outbound_") > 0)
                            {
                                model.RabbitMqTotalOutbound += messages;
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
                model.RabbitMqInbound       = int.MinValue;
                model.RabbitMqTotalOutbound = int.MinValue;
            }


            return(View(model));
        }
コード例 #4
0
ファイル: SendManager.cs プロジェクト: simongh/MantaMTA
        /// <summary>
        /// Gets the specified Send.
        /// </summary>
        /// <param name="internalSendID">Internal ID of the Send.</param>
        /// <returns>The Send.</returns>
        public async Task <Send> GetSendAsync(int internalSendID)
        {
            // Don't want send IDs sitting in memory for to long so clear every so often.
            if (this._SendsLastCleared.AddSeconds(10) < DateTime.UtcNow)
            {
                this.ClearSendsCache();
            }

            Send snd;

            // Try to get the send id from the cached collection.
            if (!this._SendsInternalID.TryGetValue(internalSendID, out snd))
            {
                // Doesn't exist so need to create or load from datbase.
                snd = await SendDB.GetSendAsync(internalSendID);

                // Add are new item to the cache.
                this._SendsInternalID.TryAdd(internalSendID, snd);
                this._Sends.TryAdd(snd.ID, snd);
            }

            return(snd);
        }
コード例 #5
0
ファイル: SendManager.cs プロジェクト: simongh/MantaMTA
        /// <summary>
        /// Gets Send with the specified sendID.
        /// If it doesn't exist it will be created in the database.
        /// </summary>
        /// <param name="sendId">ID of the Send.</param>
        /// <returns>The Send.</returns>
        public async Task <Send> GetSendAsync(string sendId)
        {
            // Don't want send IDs sitting in memory for to long so clear every so often.
            if (this._SendsLastCleared.AddSeconds(60) < DateTime.UtcNow)
            {
                this.ClearSendsCache();
            }

            Send snd;

            // Try to get the send id from the cached collection.
            if (!this._Sends.TryGetValue(sendId, out snd))
            {
                // Doesn't exist so need to create or load from datbase.
                snd = await SendDB.CreateAndGetInternalSendIDAsync(sendId).ConfigureAwait(false);

                // Add are new item to the cache.
                this._Sends.TryAdd(sendId, snd);
                this._SendsInternalID.TryAdd(snd.InternalID, snd);
            }

            // return the value.
            return(snd);
        }
コード例 #6
0
        public void TimedOutInQueue()
        {
            using (System.Transactions.TransactionScope ts = CreateTransactionScopeObject())
            {
                MantaEventCollection events = EventsManager.Instance.GetEvents();
                int initialMaxEventID       = events.Max(e => e.ID);


                bool result = false;
                EmailProcessingDetails processingDetails;

                result = EventsManager.Instance.ProcessSmtpResponseMessage(MtaParameters.TIMED_OUT_IN_QUEUE_MESSAGE, "*****@*****.**", 1, out processingDetails);
                Assert.IsTrue(result);
                Assert.IsNull(processingDetails);



                // Check an Event has been created.
                events = EventsManager.Instance.GetEvents();
                int newMaxEventID = events.Max(e => e.ID);

                Assert.AreNotEqual(initialMaxEventID, newMaxEventID);


                MantaEvent ev = events.First(e => e.ID == newMaxEventID);

                // Check the new Event record.
                Assert.IsTrue(ev is MantaTimedOutInQueueEvent);
                Assert.AreEqual(ev.ID, newMaxEventID);
                Assert.AreEqual(ev.EventType, MantaEventType.TimedOutInQueue);
                Assert.AreEqual(ev.EmailAddress, "*****@*****.**");
                Assert.AreEqual(ev.SendID, SendDB.GetSend(1).ID);
                Assert.That(ev.EventTime, Is.EqualTo(DateTime.UtcNow).Within(TimeSpan.FromSeconds(1)));                 // Depends on how long it takes to get the Events out of the DB.
                Assert.IsFalse(ev.Forwarded);
            }
        }
コード例 #7
0
ファイル: SendManager.cs プロジェクト: simongh/MantaMTA
 /// <summary>
 /// Sets the status of the specified send to the specified status.
 /// </summary>
 /// <param name="sendID">ID of the send to set the staus of.</param>
 /// <param name="status">The status to set the send to.</param>
 public void SetSendStatus(string sendID, SendStatus status)
 {
     SendDB.SetSendStatus(sendID, status);
 }
コード例 #8
0
 /// <summary>
 /// Sets the status of the specified send to the specified status.
 /// </summary>
 /// <param name="sendID">ID of the send to set the staus of.</param>
 /// <param name="status">The status to set the send to.</param>
 internal void SetSendStatus(string sendID, SendStatus status)
 {
     SendDB.SetSendStatus(sendID, status);
 }
コード例 #9
0
        /// <summary>
        /// Looks through a feedback loop email looking for something to identify it as an abuse report and who it relates to.
        /// If found, logs the event.
        ///
        /// How to get the info depending on the ESP (and this is likely to be the best order to check for each too):
        /// Abuse Report Original-Mail-From.										[Yahoo]
        /// Message-ID from body part child with content-type of message/rfc822.	[AOL]
        /// Return-Path in main message headers.									[Hotmail]
        /// </summary>
        /// <param name="message">The feedback look email.</param>
        public EmailProcessingDetails ProcessFeedbackLoop(string content)
        {
            EmailProcessingDetails processingDetails = new EmailProcessingDetails();

            MimeMessage message = MimeMessage.Parse(content);

            if (message == null)
            {
                processingDetails.ProcessingResult = EmailProcessingResult.ErrorContent;
                return(processingDetails);
            }

            try
            {
                // Step 1: Yahoo! provide useable Abuse Reports (AOL's are all redacted).
                //Look for abuse report
                BodyPart abuseBodyPart = null;
                if (this.FindFirstBodyPartByMediaType(message.BodyParts, "message/feedback-report", out abuseBodyPart))
                {
                    // Found an abuse report body part to examine.

                    // Abuse report content may have long lines whitespace folded.
                    string abuseReportBody = MimeMessage.UnfoldHeaders(abuseBodyPart.GetDecodedBody());
                    using (StringReader reader = new StringReader(abuseReportBody))
                    {
                        while (reader.Peek() > -1)
                        {
                            string line = reader.ReadToCrLf();

                            // The original mail from value will be the return-path we'd set so we should be able to get all the values we need from that.
                            if (line.StartsWith("Original-Mail-From:", StringComparison.OrdinalIgnoreCase))
                            {
                                string tmp = line.Substring("Original-Mail-From: ".Length - 1);
                                try
                                {
                                    int    internalSendID = -1;
                                    string rcptTo         = string.Empty;

                                    if (ReturnPathManager.TryDecode(tmp, out rcptTo, out internalSendID))
                                    {
                                        // NEED TO LOG TO DB HERE!!!!!
                                        Send snd = SendDB.GetSend(internalSendID);
                                        Save(new MantaAbuseEvent
                                        {
                                            EmailAddress = rcptTo,
                                            EventTime    = DateTime.UtcNow,
                                            EventType    = MantaEventType.Abuse,
                                            SendID       = (snd == null ? string.Empty : snd.ID)
                                        });

                                        processingDetails.ProcessingResult = EmailProcessingResult.SuccessAbuse;
                                        return(processingDetails);
                                    }
                                }
                                catch (Exception)
                                {
                                    // Must be redacted
                                    break;
                                }
                            }
                        }
                    }
                }
                // Function to use against BodyParts to find a return-path header.
                Func <MessageHeaderCollection, bool> checkForReturnPathHeaders = delegate(MessageHeaderCollection headers)
                {
                    MessageHeader returnPathHeader = headers.GetFirstOrDefault("Return-Path");

                    if (returnPathHeader != null && !string.IsNullOrWhiteSpace(returnPathHeader.Value))
                    {
                        int    internalSendID = -1;
                        string rcptTo         = string.Empty;
                        if (ReturnPathManager.TryDecode(returnPathHeader.Value, out rcptTo, out internalSendID))
                        {
                            if (!rcptTo.StartsWith("redacted@", StringComparison.OrdinalIgnoreCase))
                            {
                                // NEED TO LOG TO DB HERE!!!!!
                                Send snd = SendDB.GetSend(internalSendID);
                                Save(new MantaAbuseEvent
                                {
                                    EmailAddress = rcptTo,
                                    EventTime    = DateTime.UtcNow,
                                    EventType    = MantaEventType.Abuse,
                                    SendID       = (snd == null ? string.Empty : snd.ID)
                                });
                                return(true);
                            }
                        }
                    }

                    MessageHeader messageIdHeader = headers.GetFirstOrDefault("Message-ID");
                    if (messageIdHeader != null && messageIdHeader.Value.Length > 33)
                    {
                        string tmp = messageIdHeader.Value.Substring(1, 32);
                        Guid   messageID;
                        if (Guid.TryParse(tmp, out messageID))
                        {
                            int    internalSendID = -1;
                            string rcptTo         = string.Empty;
                            tmp = ReturnPathManager.GetReturnPathFromMessageID(messageID);
                            if (ReturnPathManager.TryDecode(tmp, out rcptTo, out internalSendID))
                            {
                                // NEED TO LOG TO DB HERE!!!!!
                                Send snd = SendDB.GetSend(internalSendID);
                                Save(new MantaAbuseEvent
                                {
                                    EmailAddress = rcptTo,
                                    EventTime    = DateTime.UtcNow,
                                    EventType    = MantaEventType.Abuse,
                                    SendID       = (snd == null ? string.Empty : snd.ID)
                                });
                                return(true);
                            }
                        }
                    }

                    return(false);
                }
                ;


                // Step 2: AOL give redacted Abuse Reports but include the original email as a bodypart; find that.
                BodyPart childMessageBodyPart;
                if (FindFirstBodyPartByMediaType(message.BodyParts, "message/rfc822", out childMessageBodyPart))
                {
                    if (childMessageBodyPart.ChildMimeMessage != null)
                    {
                        if (checkForReturnPathHeaders(childMessageBodyPart.ChildMimeMessage.Headers))
                        {
                            processingDetails.ProcessingResult = EmailProcessingResult.SuccessAbuse;
                            return(processingDetails);
                        }
                    }
                }


                // Step 3: Hotmail don't do Abuse Reports, they just return our email to us exactly as we sent it.
                if (checkForReturnPathHeaders(message.Headers))
                {
                    processingDetails.ProcessingResult = EmailProcessingResult.SuccessAbuse;
                    return(processingDetails);
                }
            }
            catch (Exception) { }

            Logging.Debug("Failed to find return path!");

            processingDetails.ProcessingResult = EmailProcessingResult.ErrorNoReturnPath;
            return(processingDetails);
        }
コード例 #10
0
        /// <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);
        }
コード例 #11
0
        /// <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);
        }
コード例 #12
0
ファイル: SendsController.cs プロジェクト: simongh/MantaMTA
        //
        // GET: /Sends/Overview?sendID=
        public ActionResult Overview(string sendID)
        {
            SendInfo send = SendDB.GetSend(sendID);

            return(View(new SendReportOverview(send)));
        }
コード例 #13
0
ファイル: SendsController.cs プロジェクト: simongh/MantaMTA
        //
        // GET: /Queue/
        public ActionResult Queue()
        {
            SendInfoCollection sends = SendDB.GetSendsInProgress();

            return(View(new SendsModel(sends, 1, 1)));
        }