Example #1
0
        public IActionResult EditListPost(string id, MailListModel model, string action)
        {
            if (action == "delete")
            {
                return(EditListDelete(id));
            }

            try
            {
                model.Value.Name = model.Value.Name?.Trim();
                if (model.Value.Name.Length > 16)
                {
                    throw new ArgumentException(Resources.NameIsTooLong.FormatHtml(16));
                }
                else if (!model.Value.FromEmailAddress.TryParseEmailAddress(out _))
                {
                    throw new ArgumentException(Resources.EmailIsInvalid);
                }
                model.Value.Company = model.Value.Company?.Trim();
                model.Value.Website = model.Value.Website?.Trim();
                if (!MailTemplate.ValidateName(model.Value.Name))
                {
                    throw new ArgumentException(Resources.NameInvalidChars);
                }
                using (MailDemonDatabase db = dbProvider.GetDatabase())
                {
                    MailList existingList = db.Lists.FirstOrDefault(l => l.Name == model.Value.Name);
                    if (existingList != null && (existingList.Name != model.Value.Name || model.Value.Id == 0))
                    {
                        throw new ArgumentException(Resources.NameCannotChange);
                    }
                    if (model.Value.Id == 0)
                    {
                        db.Lists.Add(model.Value);
                    }
                    else
                    {
                        db.Update(model.Value);
                    }
                    db.SaveChanges();
                }
                TempData["Message"] = Resources.Success;
                return(RedirectToAction(nameof(EditList), new { id = model.Value.Name }));
            }
            catch (Exception ex)
            {
                MailDemonLog.Error(ex);
                model.Error   = true;
                model.Message = ex.Message;
                return(View(model));
            }
        }
Example #2
0
        public IActionResult Subscribers(string id)
        {
            id = (id ?? string.Empty).Trim();
            if (id.Length == 0)
            {
                return(NotFound());
            }
            using MailDemonDatabase db = dbProvider.GetDatabase();
            MailList list = db.Lists.FirstOrDefault(l => l.Name == id);

            if (list == null)
            {
                return(NotFound());
            }
            ICollection <MailListSubscription> subscribers = db.Subscriptions.Where(s => s.ListName == id).OrderByDescending(s => s.Result).ThenByDescending(s => s.Id).Take(1000).ToList();

            ViewBag.ListName = id;
            return(View(subscribers));
        }
Example #3
0
 private IActionResult EditListDelete(string id)
 {
     try
     {
         using MailDemonDatabase db = dbProvider.GetDatabase();
         MailList list = db.Lists.FirstOrDefault(l => l.Name == id);
         if (list != null)
         {
             db.Subscriptions.RemoveRange(db.Subscriptions.Where(r => r.ListName == id));
             db.Templates.RemoveRange(db.Templates.Where(t => t.Name.StartsWith(list.Name + MailTemplate.FullNameSeparator)));
             db.Lists.Remove(list);
             db.SaveChanges();
         }
     }
     catch (Exception ex)
     {
         MailDemonLog.Error(ex);
     }
     return(RedirectToAction(nameof(EditList)));
 }
Example #4
0
        /// <summary>
        /// Bulk email
        /// </summary>
        /// <param name="db">Database</param>
        /// <param name="list">List</param>
        /// <param name="unsubscribeUrl">Unsubscribe url</param>
        /// <param name="all">True to email all, false to only email error registrations (those that have not yet or failed to send)</param>
        /// <returns>Subscriptions to send to, grouped by domain</returns>
        public static IEnumerable <KeyValuePair <string, List <MailListSubscription> > > BeginBulkEmail(this MailDemonDatabase db, MailList list, string unsubscribeUrl, bool all)
        {
            if (all)
            {
                db.Database.ExecuteSqlRaw("UPDATE Subscriptions SET Result = 'Pending', ResultTimestamp = {0} WHERE ListName = {1}", DateTime.UtcNow, list.Name);
            }
            else
            {
                db.Database.ExecuteSqlRaw("UPDATE Subscriptions SET Result = 'Pending', ResultTimestamp = {0} WHERE ListName = {1} AND Result <> ''", DateTime.UtcNow, list.Name);
            }
            List <MailListSubscription> subs = new List <MailListSubscription>();
            string domain = null;

            foreach (MailListSubscription sub in db.Subscriptions.Where(s => s.ListName == list.Name && s.Result == "Pending")
                     .OrderBy(s => s.EmailAddressDomain))
            {
                if (domain is null || sub.EmailAddressDomain != domain)
                {
                    if (subs.Count != 0)
                    {
                        yield return(new KeyValuePair <string, List <MailListSubscription> >(domain, subs));

                        subs = new List <MailListSubscription>();
                    }
                    domain = sub.EmailAddressDomain;
                }
                sub.MailList       = list;
                sub.UnsubscribeUrl = string.Format(unsubscribeUrl, sub.UnsubscribeToken);
                subs.Add(sub);
            }
            if (subs.Count != 0)
            {
                yield return(new KeyValuePair <string, List <MailListSubscription> >(domain, subs));
            }
        }
Example #5
0
        public async Task SendBulkMail(MailList list, IMailCreator mailCreator, IMailSender mailSender, ExpandoObject viewBag,
                                       bool all, string fullTemplateName, string unsubscribeUrl)
        {
            MailDemonLog.Warn("Started bulk send for {0}", fullTemplateName);

            DateTime    now          = DateTime.UtcNow;
            int         successCount = 0;
            int         failCount    = 0;
            List <Task> pendingTasks = new List <Task>();
            Stopwatch   timer        = Stopwatch.StartNew();

            using (var db = dbProvider.GetDatabase())
            {
                void callbackHandler(MailListSubscription _sub, string error)
                {
                    lock (db)
                    {
                        // although this is slow, it is required as we do not want to double email people in the event
                        // that server reboots, loses power, etc. for every message we have to mark that person
                        // with the correct status immediately
                        _sub.Result          = error;
                        _sub.ResultTimestamp = DateTime.UtcNow;
                        db.Update(_sub);
                        db.SaveChanges();
                        if (string.IsNullOrWhiteSpace(error))
                        {
                            successCount++;
                        }
                        else
                        {
                            failCount++;
                        }
                    }
                }

                // use a separate database instance to do the query, that way we can update records in our other database instance
                // preventing locking errors, especially with sqlite drivers
                MailDemonLog.Warn("Begin bulk send");
                using (var dbBulk = dbProvider.GetDatabase())
                {
                    IEnumerable <KeyValuePair <string, IEnumerable <MailListSubscription> > > pendingSubs = dbBulk.GetBulkEmailSubscriptions(list, unsubscribeUrl, all);
                    foreach (KeyValuePair <string, IEnumerable <MailListSubscription> > sub in pendingSubs)
                    {
                        now = DateTime.UtcNow;
                        try
                        {
                            IAsyncEnumerable <MailToSend> messagesToSend = GetMessages(sub.Value, mailCreator, list, viewBag, fullTemplateName, callbackHandler);
                            Task task = mailSender.SendMailAsync(sub.Key, messagesToSend);
                            pendingTasks.Add(task);
                        }
                        catch (Exception ex)
                        {
                            MailDemonLog.Error(ex);
                        }
                    }
                }

                await Task.WhenAll(pendingTasks);

                MailDemonLog.Warn("Finished bulk send for {0}, {1} messages succeeded, {2} messages failed in {3:0.00} seconds.", fullTemplateName, successCount, failCount, timer.Elapsed.TotalSeconds);
            }

            GC.Collect();
        }
Example #6
0
 private async IAsyncEnumerable <MailToSend> GetMessages(IEnumerable <MailListSubscription> subs, IMailCreator mailCreator, MailList list,
                                                         ExpandoObject viewBag, string fullTemplateName, Action <MailListSubscription, string> callback)
 {
     foreach (MailListSubscription sub in subs)
     {
         MimeMessage message;
         try
         {
             message = await mailCreator.CreateMailAsync(fullTemplateName, sub, viewBag, null);
         }
         catch (Exception ex)
         {
             MailDemonLog.Error(ex);
             continue;
         }
         message.From.Clear();
         message.To.Clear();
         if (string.IsNullOrWhiteSpace(list.FromEmailName))
         {
             message.From.Add(MailboxAddress.Parse(list.FromEmailAddress));
         }
         else
         {
             message.From.Add(new MailboxAddress(list.FromEmailName, list.FromEmailAddress));
         }
         message.To.Add(MailboxAddress.Parse(sub.EmailAddress));
         yield return(new MailToSend {
             Subscription = sub, Message = message, Callback = callback
         });
     }
 }