public IActionResult Lists() { using (MailDemonDatabase db = dbProvider.GetDatabase()) { return(View(db.Lists.OrderBy(l => l.Name).ToArray())); } }
public IActionResult Unsubscribe(string id, string token) { id = (id ?? string.Empty).Trim(); if (id.Length == 0) { return(NotFound()); } int pos = id.IndexOf('@'); if (pos >= 0) { id = id.Substring(0, pos); } // stupid bing/outlook email preview string userAgent = Request.Headers["User-Agent"].ToString(); if (string.IsNullOrWhiteSpace(userAgent) || userAgent.Contains("preview", StringComparison.OrdinalIgnoreCase)) { return(Content(string.Empty)); } token = (token ?? string.Empty).Trim(); using (MailDemonDatabase db = dbProvider.GetDatabase()) { if (db.UnsubscribeFromMailingList(id, token)) { string success = Resources.UnsubscribeSuccess.FormatHtml(id); return(View((object)success)); } } string error = Resources.UnsubscribeError.FormatHtml(id); return(View((object)error)); }
private IActionResult EditTemplateSend(string id, bool all) { id = (id ?? string.Empty).Trim(); if (id.Length == 0) { return(NotFound()); } string listName = MailTemplate.GetListName(id); MailList list; using (MailDemonDatabase db = dbProvider.GetDatabase()) { list = db.Lists.FirstOrDefault(l => l.Name == listName); } if (list == null) { return(NotFound()); } if (bulkMailSender == null) { return(NotFound()); } string unsubscribeUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/{nameof(Unsubscribe)}/{id}?token={{0}}"; bulkMailSender.SendBulkMail(list, mailCreator, mailSender, GetViewBagForTemplate(), all, id, unsubscribeUrl).ConfigureAwait(false).GetAwaiter(); TempData["Message"] = Resources.SendStarted; return(RedirectToAction(nameof(HomeController.EditTemplate), new { id })); }
public IActionResult EditTemplatePost(string id, MailTemplateModel model, string action) { memoryCache?.Remove("Template_" + id); if (action == "delete") { return(EditTemplateDelete(id)); } else if (action == "send") { model.All = Request.Form.ContainsKey("All"); return(EditTemplateSend(id, model.All)); } try { model.Value.Name = model.Value.Name?.Trim(); if (model.Value.Name.Length > 64) { throw new ArgumentException(Resources.NameIsTooLong.FormatHtml(64)); } if (!model.Value.GetListNameAndTemplateName(out string listName, out string templateName) || !MailTemplate.ValidateName(listName) || !MailTemplate.ValidateName(templateName)) { throw new ArgumentException(Resources.TemplateNameInvalidChars.FormatHtml(MailTemplate.FullNameSeparator)); } using (MailDemonDatabase db = dbProvider.GetDatabase()) { if (db.Lists.FirstOrDefault(l => l.Name == listName) == null) { throw new ArgumentException(string.Format(Resources.ListNotFound, listName)); } model.Value.LastModified = DateTime.UtcNow; model.Value.Dirty = true; if (model.Value.Id == 0) { db.Templates.Add(model.Value); } else { db.Update(model.Value); } db.SaveChanges(); } TempData["Message"] = Resources.Success; return(RedirectToAction(nameof(EditTemplate), new { id = model.Value.Name })); } catch (Exception ex) { MailDemonLog.Error(ex); model.Error = true; model.Message = ex.Message; return(View(model)); } }
public IActionResult EditList(string id) { MailList list; using (MailDemonDatabase db = dbProvider.GetDatabase()) { list = db.Lists.FirstOrDefault(l => l.Name == id) ?? new MailList(); } return(View(new MailListModel { Value = list, Message = TempData["Message"] as string })); }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { do { DateTime dt = DateTime.UtcNow; using (MailDemonDatabase db = serviceProvider.GetService <MailDemonDatabase>()) { db.Subscriptions.RemoveRange(db.Subscriptions.Where(r => r.Expires <= dt && r.UnsubscribeToken == null)); db.SaveChanges(); } }while (!(await stoppingToken.WaitHandle.WaitOneAsync(loopTimeSpan, stoppingToken))); }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { DateTime dt = DateTime.UtcNow; using MailDemonDatabase db = dbProvider.GetDatabase(); db.Subscriptions.RemoveRange(db.Subscriptions.Where(r => r.Expires <= dt && r.UnsubscribeToken == null)); await db.SaveChangesAsync(); await Task.Delay(loopTimeSpan, stoppingToken); } }
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)); } }
public async Task <IActionResult> DebugTemplate(string id) { if (HttpContext.Request.Method == "POST") { // switch to GET request, to avoid stupid double form post popup return(RedirectToAction(nameof(DebugTemplate), new { id })); } id = (id ?? string.Empty).Trim(); if (id.Length == 0) { return(NotFound()); } string listName = MailTemplate.GetListName(id); MailList list; using (MailDemonDatabase db = dbProvider.GetDatabase()) { list = db.Lists.FirstOrDefault(l => l.Name == listName); } if (list == null) { return(NotFound()); } string unsubscribeToken = Guid.NewGuid().ToString("N"); MailListSubscription tempReg = new MailListSubscription { EmailAddress = "*****@*****.**", IPAddress = HttpContext.GetRemoteIPAddress().ToString(), FirstName = "Bob", LastName = "Smith", Company = "Fake Company", ListName = list.Name, SubscribedDate = DateTime.UtcNow, SubscribeToken = Guid.NewGuid().ToString("N"), Expires = DateTime.MinValue, MailList = list, UnsubscribeToken = unsubscribeToken, UnsubscribeUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/{nameof(Unsubscribe)}/{list.Name}?token={unsubscribeToken}" }; MimeMessage msg = await mailCreator.CreateMailAsync(id, tempReg, GetViewBagForTemplate(), (_html, subject) => { return(_html.Replace("<body>", "<body><div style='padding: 10px; width: 100%; background-color: #2A2A2A; border-bottom: 1px solid #444444;'>SUBJECT: " + System.Web.HttpUtility.HtmlEncode(subject) + "</div>")); }); string html = msg.HtmlBody; return(Content(msg.HtmlBody, "text/html")); }
public IActionResult Subscribers(string id, string action, long?subId) { if (action == "delete" && subId != null) { using MailDemonDatabase db = dbProvider.GetDatabase(); MailListSubscription sub = db.Subscriptions.FirstOrDefault(s => s.Id == subId); if (sub != null) { db.Subscriptions.Remove(sub); db.SaveChanges(); } } return(RedirectToAction(nameof(Subscribers), new { id })); }
/// <summary> /// Unsubscribe from a mailing list /// </summary> /// <param name="db">DB</param> /// <param name="listName">List name</param> /// <param name="token">Unsubscribe token</param> /// <returns>True if unsubscribed, false if not</returns> public static bool UnsubscribeFromMailingList(this MailDemonDatabase db, string listName, string token) { MailListSubscription foundReg = db.Subscriptions.FirstOrDefault(r => r.UnsubscribeToken == token && r.ListName == listName && r.UnsubscribedDate == default); if (foundReg != null) { foundReg.UnsubscribedDate = DateTime.UtcNow; foundReg.SubscribeToken = null; db.SaveChanges(); return(true); } ; return(false); }
public IActionResult Templates(string id) { List <MailTemplateBase> templates = new List <MailTemplateBase>(); using (MailDemonDatabase db = dbProvider.GetDatabase()) { foreach (MailTemplate template in db.Templates.Where(t => string.IsNullOrWhiteSpace(id) || t.Name.StartsWith(id + MailTemplate.FullNameSeparator))) { templates.Add(new MailTemplateBase { Id = template.Id, LastModified = template.LastModified, Name = template.Name }); } } return(View(templates.OrderBy(t => t.Name))); }
private IActionResult EditTemplateDelete(string id) { try { using MailDemonDatabase db = dbProvider.GetDatabase(); MailTemplate template = db.Templates.FirstOrDefault(t => t.Name == id); if (template != null) { db.Templates.Remove(template); db.SaveChanges(); } } catch (Exception ex) { MailDemonLog.Error(ex); } return(RedirectToAction(nameof(EditTemplate))); }
/// <summary> /// Confirm subscribe to a mailing list /// </summary> /// <param name="db">DB</param> /// <param name="listName">List name</param> /// <param name="token">Subscribe token</param> /// <returns>Registration or null if not found</returns> public static MailListSubscription ConfirmSubscribeToMailingList(this MailDemonDatabase db, string listName, string token) { MailListSubscription reg = null; MailListSubscription foundReg = db.Subscriptions.FirstOrDefault(r => r.SubscribeToken == token); if (foundReg != null && foundReg.ListName == listName && foundReg.SubscribedDate == default && foundReg.SubscribeToken == token) { reg = foundReg; foundReg.Expires = DateTime.MaxValue; foundReg.SubscribedDate = DateTime.UtcNow; foundReg.UnsubscribedDate = default; foundReg.UnsubscribeToken = Guid.NewGuid().ToString("N"); foundReg.MailList = db.Lists.FirstOrDefault(l => l.Name == listName); foundReg.Result = "New"; db.SaveChanges(); } return(reg); }
public IActionResult EditTemplate(string id) { MailTemplate template; using (MailDemonDatabase db = dbProvider.GetDatabase()) { template = db.Templates.FirstOrDefault(t => t.Name == id) ?? new MailTemplate { Text = "<!-- Subject: ReplaceWithYourSubject -->\r\n" }; } if (template.Id == 0 && string.IsNullOrWhiteSpace(template.Name) && id.IndexOf(MailTemplate.FullNameSeparator) < 0) { template.Name = id + MailTemplate.FullNameSeparator; } return(View(new MailTemplateModel { Value = template, Message = TempData["Message"] as string })); }
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)); }
private void InitializeDB(IApplicationBuilder app) { using var db = new MailDemonDatabase(Configuration); db.Initialize(); // migrate away from litedb string migrationPath = Path.Combine(Directory.GetCurrentDirectory(), "MailDemon.db"); if (File.Exists(migrationPath)) { MailDemonLog.Warn("Migrating from old database {0}", migrationPath); var tran = db.Database.BeginTransaction(); try { using (FileStream fs = File.OpenRead(migrationPath)) using (LiteDB.LiteDatabase oldDb = new LiteDB.LiteDatabase(fs)) { foreach (MailList list in oldDb.GetCollection <MailList>().FindAll()) { db.Lists.Add(list); } foreach (MailTemplate template in oldDb.GetCollection <MailTemplate>().FindAll()) { db.Templates.Add(template); } foreach (MailListSubscription sub in oldDb.GetCollection <MailListSubscription>().FindAll()) { db.Subscriptions.Add(sub); } db.SaveChanges(); tran.Commit(); tran = null; MailDemonLog.Warn("Migration success"); } File.Delete(migrationPath); } finally { tran?.Rollback(); } } }
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))); }
public IActionResult SubscribeConfirm(string id) { id = (id ?? string.Empty).Trim(); if (id.Length == 0) { return(NotFound()); } MailList list; using (MailDemonDatabase db = dbProvider.GetDatabase()) { list = db.Lists.FirstOrDefault(l => l.Name == id); } if (list == null) { return(View("SubscribeExpired")); } // the link will be sent via email return(View("SubscribeConfirmNoLink", list)); }
/// <summary> /// Pre subscribe to a mailing list /// </summary> /// <param name="db">DB</param> /// <param name="reg">Registration, receives new registration if success</param> /// <returns>True if success, false if already subscribed</returns> public static bool PreSubscribeToMailingList(this MailDemonDatabase db, ref MailListSubscription reg) { bool result = false; string token = string.Empty; MailListSubscription final = reg; MailListSubscription dbReg = db.Subscriptions.FirstOrDefault(r => r.EmailAddress == final.EmailAddress && r.ListName == final.ListName); if (dbReg != null) { dbReg.Fields.Clear(); foreach (var kv in final.Fields) { dbReg.SetField(kv.Key, kv.Value); } dbReg.Error = final.Error; dbReg.Message = final.Message; dbReg.IPAddress = final.IPAddress; dbReg.MailList = final.MailList; dbReg.TemplateName = final.TemplateName; final = dbReg; } reg = final; if (reg.SubscribeToken == null) { reg.SubscribeToken = Guid.NewGuid().ToString("N"); reg.Expires = DateTime.UtcNow.AddHours(1.0); if (reg.Id == 0) { db.Subscriptions.Add(reg); } else { db.Update(reg); } result = true; } db.SaveChanges(); return(result); }
public IActionResult SubscribeInitial(string id) { string result = TempData["result"] as string; id = (id ?? string.Empty).Trim(); if (id.Length == 0) { return(NotFound()); } MailListSubscription model = (string.IsNullOrWhiteSpace(result) ? new MailListSubscription() : JsonConvert.DeserializeObject <MailListSubscription>(result)); using (MailDemonDatabase db = dbProvider.GetDatabase()) { model.MailList = db.Lists.FirstOrDefault(l => l.Name == id); } if (model.MailList == null) { return(NotFound()); } model.ListName = id; model.TemplateName = MailTemplate.GetFullTemplateName(id, MailTemplate.NameSubscribeInitial); return(View(model)); }
public async Task <IActionResult> SubscribeWelcome(string id, string token) { id = (id ?? string.Empty).Trim(); if (id.Length == 0) { return(NotFound()); } // stupid bing/outlook email preview string userAgent = Request.Headers["User-Agent"].ToString(); if (string.IsNullOrWhiteSpace(userAgent) || userAgent.Contains("preview", StringComparison.OrdinalIgnoreCase)) { return(Content(string.Empty)); } token = (token ?? string.Empty).Trim(); MailListSubscription reg; using (MailDemonDatabase db = dbProvider.GetDatabase()) { reg = db.ConfirmSubscribeToMailingList(id, token); } if (reg == null) { return(View("SubscribeExpired")); } // temp property does not go in db reg.UnsubscribeUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/{nameof(Unsubscribe)}/{id}?token={reg.UnsubscribeToken}"; string templateFullName = MailTemplate.GetFullTemplateName(id, MailTemplate.NameSubscribeWelcome); await SendMailAsync(reg, templateFullName); return(View(reg)); }
/// <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)); } }
public async Task <IActionResult> SubscribeInitialPost(string id, Dictionary <string, string> formFields) { id = (id ?? string.Empty).Trim(); if (id.Length == 0) { return(NotFound()); } string error = null; if (RequireCaptcha) { formFields.TryGetValue("captcha", out string captchaValue); error = await MailDemonWebApp.Instance.Recaptcha.Verify(Request.GetDisplayUrl(), captchaValue, nameof(SubscribeInitial), HttpContext.GetRemoteIPAddress().ToString(), formFields); } MailListSubscription model = new MailListSubscription { Message = error, Error = !string.IsNullOrWhiteSpace(error) }; MailList list; using (MailDemonDatabase db = dbProvider.GetDatabase()) { list = db.Lists.FirstOrDefault(l => l.Name == id); } if (list == null) { return(NotFound()); } model.MailList = list; model.ListName = model.MailList.Name; if (formFields.ContainsKey("TemplateName")) { model.TemplateName = formFields["TemplateName"]; } string email = null; foreach (KeyValuePair <string, string> field in formFields) { if (field.Key.StartsWith("ff_")) { string value = field.Value?.Trim(); string name = field.Key.Split('_')[1]; if (string.IsNullOrWhiteSpace(value)) { if (field.Key.EndsWith("_optional", StringComparison.OrdinalIgnoreCase)) { model.Fields[name] = string.Empty; } else { model.Message += "<br/>" + field.Key.Split('_')[1] + " is required"; model.Error = true; } } else if (name.Contains("email", StringComparison.OrdinalIgnoreCase)) { if (value.TryParseEmailAddress(out _)) { email = (email ?? value); model.EmailAddress = email; } else { model.Error = true; } } else { model.Fields[name] = value; } } } model.EmailAddressDomain = model.GetDomainFromEmailAddress(); TempData["result"] = JsonConvert.SerializeObject(model); if (model.Error || email == null) { if (email == null) { model.Error = true; model.Message += "<br/>" + Resources.EmailIsInvalid; } return(View(nameof(SubscribeInitial), model)); } else { try { model.IPAddress = HttpContext.GetRemoteIPAddress().ToString(); using (MailDemonDatabase db = dbProvider.GetDatabase()) { if (!db.PreSubscribeToMailingList(ref model)) { throw new InvalidOperationException(Resources.AlreadySubscribed.FormatHtml(id)); } } string url = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/{nameof(SubscribeWelcome)}/{id}?token={model.SubscribeToken}"; model.SubscribeUrl = url; string templateFullName = MailTemplate.GetFullTemplateName(id, MailTemplate.NameSubscribeConfirm); await SendMailAsync(model, templateFullName); return(RedirectToAction(nameof(SubscribeConfirm), new { id = model.ListName })); } catch (Exception ex) { MailDemonLog.Error(ex); model.Error = true; model.Message += "<br/>" + ex.Message; return(View(nameof(SubscribeInitial), model)); } } }