diff --git a/Pages/Contact.cshtml.cs b/Pages/Contact.cshtml.cs index ba6af89..b776cce 100644 --- a/Pages/Contact.cshtml.cs +++ b/Pages/Contact.cshtml.cs @@ -1,13 +1,14 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc.RazorPages; +using System; using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using MailKit.Net.Smtp; +using MailKit.Security; using Microsoft.AspNetCore.Mvc; -using System.Net.Mail; -using System.Net; -//for captcha: -using System.Net.Http; -using Newtonsoft.Json.Linq; +using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using MimeKit; namespace contact.Pages { @@ -28,137 +29,31 @@ namespace contact.Pages public string Email { get; set; } [Required] public string Message { get; set; } - } public class ContactModel : PageModel { + private const string ConfirmedUrl = "https://ayanova.com/confirmed.htm"; + + //2022-05-06 Company names containing any of these are dropped silently but still get the + //success redirect, so the spammer gets no signal and has to pay someone to enter them by hand + private static readonly string[] SpamBlocks = { "google", "jumboleadmagnet", "boostleadgeneration" }; + public string Message { get; set; } + [BindProperty(SupportsGet = true)] public ContactFormModel Contact { get; set; } + //Set when the send fails, so Contact.cshtml can tell the visitor instead of claiming success + public bool SendFailed { get; set; } + private readonly IConfiguration _configuration; - public ContactModel(IConfiguration configuration) + private readonly ILogger _logger; + + public ContactModel(IConfiguration configuration, ILogger logger) { _configuration = configuration; - } - - public ActionResult OnPost() - { - if (!ModelState.IsValid) - { - return Page(); - } - - - //2025-11-01 Google dropping support for captcha, just nuking it for now, see how much spam I get - // if (!ReCaptchaPassed( - // Request.Form["g-recaptcha-response"], // that's how you get it from the Request object - // _configuration.GetSection("GoogleReCaptcha:secret").Value - // )) - // { - // //Return a fail code that will hopefully take us off the spammers list - // return StatusCode(500); - // } - - - - //go away spammers - //2022-05-06 changes to get rid of jumbo lead magnet spammer but still force him to pay someone to manually enter the stuff - //ideally it should read from a text file here but this is a quickie fix while Joyce is on important viha phone call - var blocks = new List(); - blocks.Add("google"); - blocks.Add("jumboleadmagnet");//<--go away this guy in particular, keeps spamming - blocks.Add("boostleadgeneration"); - //ericjonesmyemail@gmail.com - var companyname = Contact.Company.ToLowerInvariant(); - foreach (string s in blocks) - { - if (companyname.Contains(s)) - { - goto skipspam; - - } - } - - SendMail(); - - skipspam: - //TODO: go to a successful submit page on the ayanova site - //return Redirect("/confirm.html");//sample quickie page I made up - return Redirect("https://ayanova.com/confirmed.htm"); - } - - - private void SendMail() - { - var MessageBody = $"Name:\r\n{Contact.Name}\r\n\r\nCompany:\r\n{Contact.Company}\r\n\r\nDatabase Id:\r\n{Contact.DbId}\r\n\r\nMessage:\r\n{Contact.Message}"; - - //SEND TO US - using (var message = new MailMessage()) - { - message.To.Add(new MailAddress("support@ayanova.com")); - message.From = new MailAddress(Contact.Email); - message.Subject = $"Question about AyaNova from {Contact.Company}"; - message.Body = MessageBody; - using (var smtpClient = new SmtpClient("smtp.fastmail.com")) - { - - smtpClient.Host = "smtp.fastmail.com"; - smtpClient.Port = 587; - smtpClient.UseDefaultCredentials = false; - //testing - smtpClient.EnableSsl = true; - - // smtpClient.Port=465; - // smtpClient.DeliveryMethod= SmtpDeliveryMethod.Network; - - - - //NOTE: Do not use the noreply email address to send mail, it crashes the server somehow - - //****************************************************************************************************************** - //****************************************************************************************************************** - //TEST CRASH - // smtpClient.Credentials = new System.Net.NetworkCredential("noreply@ayanova.com", "91768700489f8edd28aa71e3e0f4073eba54ce83c4c1a6a910700fa94094ddfd"); - - - //FASTMAIL CHANGES - //Fastmail server names and ports help page: https://www.fastmail.help/hc/en-us/articles/1500000278342 - //APP Password created in fastmail for smtp use only labelled 'contact-form': cnea8agl6nlx5j7l - - //try using from as support@onayanova.com - //try domain version if this still won't work - smtpClient.Credentials = new System.Net.NetworkCredential("support@onayanova.com", "cnea8agl6nlx5j7l"); - //****************************************************************************************************************** - //****************************************************************************************************************** - - smtpClient.Send(message); - } - } - - //Removed 2022-10-06 causing backscatter and also not helpful - // //SEND COPY TO CLIENT - // using (var message = new MailMessage()) - // { - // message.To.Add(new MailAddress(Contact.Email)); - // message.From = new MailAddress("support@ayanova.com"); - // message.Subject = $"Confirmation that your question about AyaNova has been received from {Contact.Company}"; - // message.Body = MessageBody; - - // using (var smtpClient = new SmtpClient("smtp.fastmail.com")) - // { - - // smtpClient.Host = "smtp.fastmail.com"; - // smtpClient.Port = 587; - // smtpClient.UseDefaultCredentials = false; - // smtpClient.EnableSsl=true; - // //NOTE: Do not use the noreply email address to send mail, it crashes the server somehow - // smtpClient.Credentials = new System.Net.NetworkCredential("support@onayanova.com", "cnea8agl6nlx5j7l"); - // smtpClient.Send(message); - // } - // } - + _logger = logger; } public void OnGet() @@ -166,28 +61,64 @@ namespace contact.Pages Message = "Your contact page."; } + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } - //2025-11-01 Google dropping support for captcha, just nuking it for now, see how much spam I get - // public static bool ReCaptchaPassed(string gRecaptchaResponse, string secret) - // { - // HttpClient httpClient = new HttpClient(); - // var res = httpClient.GetAsync($"https://www.google.com/recaptcha/api/siteverify?secret={secret}&response={gRecaptchaResponse}").Result; - // if (res.StatusCode != HttpStatusCode.OK) - // { - // // logger.LogError("Error while sending request to ReCaptcha"); - // return false; - // } + //2025-11-01 reCAPTCHA removed (Google service change). Replacement deferred, see runbook Phase 6. - // string JSONres = res.Content.ReadAsStringAsync().Result; - // dynamic JSONdata = JObject.Parse(JSONres); - // if (JSONdata.success != "true") - // { - // return false; - // } + var company = Contact.Company.ToLowerInvariant(); + if (SpamBlocks.Any(s => company.Contains(s))) + { + return Redirect(ConfirmedUrl); + } - // return true; - // } + try + { + await SendMailAsync(); + } + catch (Exception ex) + { + //Log the whole submission so it can be recovered from the journal, not lost + _logger.LogError(ex, + "Contact form send failed. Name: {Name} | Company: {Company} | Email: {Email} | DbId: {DbId} | Message: {Message}", + Contact.Name, Contact.Company, Contact.Email, Contact.DbId, Contact.Message); + SendFailed = true; + return Page(); + } + return Redirect(ConfirmedUrl); + } + private async Task SendMailAsync() + { + var smtp = _configuration.GetSection("Smtp"); + + //Email is in the body as well as Reply-To, so it survives even if Reply-To can't be set + var body = $"Name:\r\n{Contact.Name}\r\n\r\nCompany:\r\n{Contact.Company}\r\n\r\nEmail:\r\n{Contact.Email}\r\n\r\nDatabase Id:\r\n{Contact.DbId}\r\n\r\nMessage:\r\n{Contact.Message}"; + + var message = new MimeMessage(); + //From is always the authenticated account; the visitor goes in Reply-To (no sender spoofing) + message.From.Add(new MailboxAddress("AyaNova contact form", smtp["User"])); + message.To.Add(MailboxAddress.Parse("support@ayanova.com")); + if (MailboxAddress.TryParse(Contact.Email, out var replyTo)) + { + replyTo.Name = Contact.Name; + message.ReplyTo.Add(replyTo); + } + message.Subject = $"Question about AyaNova from {OneLine(Contact.Company)}"; + message.Body = new TextPart("plain") { Text = body }; + + using var client = new SmtpClient(); + await client.ConnectAsync(smtp["Host"], smtp.GetValue("Port"), SecureSocketOptions.StartTls); + await client.AuthenticateAsync(smtp["User"], smtp["Password"]); + await client.SendAsync(message); + await client.DisconnectAsync(true); + } + + private static string OneLine(string s) => s.Replace("\r", " ").Replace("\n", " "); } } diff --git a/Program.cs b/Program.cs index a6518bb..b354103 100644 --- a/Program.cs +++ b/Program.cs @@ -1,19 +1,14 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; namespace contact { public class Program { - public static void Main(string[] args) - { - CreateWebHostBuilder(args).Build().Run(); - } - - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseUrls("http://*:3002")//only this port works with nginx proxy - //.UseUrls("http://localhost:3002")//this doesn't work with nginx proxy - .UseStartup(); + public static void Main(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(web => web.UseStartup()) + .Build() + .Run(); } } diff --git a/appsettings.Development.json b/appsettings.Development.json index a2880cb..143621e 100644 --- a/appsettings.Development.json +++ b/appsettings.Development.json @@ -5,5 +5,6 @@ "System": "Information", "Microsoft": "Information" } - } + }, + "AllowedHosts": "localhost" } diff --git a/contact.csproj b/contact.csproj index 48a82e2..691b674 100644 --- a/contact.csproj +++ b/contact.csproj @@ -1,20 +1,9 @@ - - + - net6.0 + net10.0 + efc95c93-c628-4ad5-a697-cd1613181a44 - - - + + - - - + \ No newline at end of file