using HtmlAgilityPack; using log4net; using MailSend.Common; using MailSend.Common.Models; using MailSend.Web.Models; using MimeKit; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Configuration; using System.IO; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; namespace MailSend.Web.Controllers { public class MailController : Controller { private static ILog logger = LogManager.GetLogger("MailController"); private MailDataContext mailData = new MailDataContext(); //发送邮件 [HttpPost] public ActionResult Send() { var resp = new RespCommon(); StreamReader sr = new StreamReader(Request.InputStream, Encoding.UTF8); var strJson = sr.ReadToEnd(); if (string.IsNullOrEmpty(strJson)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "参数错误"; return Json(resp); } logger.Debug($"收到邮件发送请求:{strJson}"); var smtpList = mailData.MailSendSmtp.AsNoTracking() .Select(x => x.GID) .ToList(); List listToSend = null; try { listToSend = JsonConvert.DeserializeObject>(strJson); //校验smtp参数 foreach (var item in listToSend) { //没有SmtpConfig参数时,需要提供发件邮箱信息 if (string.IsNullOrEmpty(item.SmtpConfig)) { if (string.IsNullOrEmpty(item.Account) || string.IsNullOrEmpty(item.Password) || string.IsNullOrEmpty(item.Server) || item.Port == 0) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "发件邮箱信息无效"; return Json(resp); } else { var smtpDB = mailData.MailSendSmtp.FirstOrDefault(x => x.Account == item.Account); if (smtpDB == null) { smtpDB = new MailSendSmtp(); smtpDB.GID = Guid.NewGuid().ToString(); smtpDB.Account = item.Account; mailData.MailSendSmtp.Add(smtpDB); } smtpDB.Password = item.Password; smtpDB.Port = item.Port; smtpDB.Server = item.Server; smtpDB.UseSSL = item.UseSSL; mailData.SaveChanges(); item.SmtpConfig = smtpDB.GID; } } else { if (!smtpList.Contains(item.SmtpConfig)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "SmtpConfig无效"; return Json(resp); } } } } catch { logger.Error($"JSON格式有误,解析失败:{strJson}"); resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "JSON格式有误,解析失败"; return Json(resp); } foreach (var item in listToSend) { if (string.IsNullOrEmpty(item.SendTo)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "接收人不能为空"; return Json(resp); } if (string.IsNullOrEmpty(item.Title)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "标题不能为空"; return Json(resp); } if (string.IsNullOrEmpty(item.Body)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "正文不能为空"; return Json(resp); } var mailSend = new Common.MailSend(); mailSend.GID = Guid.NewGuid().ToString().Replace("-", ""); mailSend.SendTo = item.SendTo; mailSend.CCTo = item.CCTo; mailSend.Title = item.Title; mailSend.Body = item.Body; mailSend.SmtpConfig = item.SmtpConfig; mailSend.ShowName = item.ShowName; //附件处理 if (item.Attaches != null && item.Attaches.Count > 0) { var list = new List(); foreach (var att in item.Attaches) { if (string.IsNullOrEmpty(att.AttachName)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = $"附件名称不能为空"; return Json(resp); } if (string.IsNullOrEmpty(att.AttachContent)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = $"附件内容不能为空:{att.AttachName}"; return Json(resp); } try { var bsArr = Convert.FromBase64String(att.AttachContent); var ext = Path.GetExtension(att.AttachName); var fileTmpName = Guid.NewGuid().ToString(); var tempPath = Server.MapPath("~/Temp"); if (!Directory.Exists(tempPath)) { Directory.CreateDirectory(tempPath); } var fileFullPath = Path.Combine(tempPath, fileTmpName + ext); System.IO.File.WriteAllBytes(fileFullPath, bsArr); var attModel = new AttachFileModel(); attModel.FileName = att.AttachName; attModel.FilePath = fileFullPath; list.Add(attModel); } catch (Exception ex) { logger.Error($"附件还原失败:{ex.Message}"); logger.Error(ex.StackTrace); resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = $"附件还原失败:{att.AttachName}"; return Json(resp); } } mailSend.AttachFiles = JsonConvert.SerializeObject(list); } mailData.MailSend.Add(mailSend); } mailData.SaveChanges(); resp.Success = true; resp.Code = RespCommon.RespCodeSuccess; resp.Message = "提交成功"; return Json(resp); } //转发邮件 [HttpPost] public ActionResult Transmit() { var resp = new RespCommon(); //需上传eml文件 if (Request.Files.Count > 0) { var smtpList = mailData.MailSendSmtp.AsNoTracking() .Select(x => x.GID) .ToList(); var sendTo = Request.Form["SendTo"]; var ccTo = Request.Form["CcTo"]; var smtpConfig = Request.Form["SmtpConfig"]; //SmtpConfig var mailAccount = Request.Form["MailAccount"]; var password = Request.Form["Password"]; int.TryParse(Request.Form["Port"], out int port); var smtpServer = Request.Form["SmtpServer"]; bool.TryParse(Request.Form["SmtpSSL"], out bool smtpSSL); var showName = Request.Form["ShowName"]; var prefixText = Request.Form["PrefixText"]; //前缀文本 if (string.IsNullOrEmpty(sendTo)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "收件人不能为空"; return Json(resp); } //分拆收件邮箱地址 var sendToList = new List(); var arrTo = sendTo.Replace(",", ";").Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); foreach (var item in arrTo) { sendToList.Add(new MailboxAddress(item, item)); } //分拆cc邮箱地址 var ccList = new List(); if (!string.IsNullOrEmpty(ccTo)) { var arrCc = ccTo.Replace(",", ";").Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); foreach (var item in arrCc) { ccList.Add(new MailboxAddress(item, item)); } } MailSendSmtp smtpInfo = null; //没有SmtpConfig参数时,需要提供发件邮箱信息 if (string.IsNullOrEmpty(smtpConfig)) { if (string.IsNullOrEmpty(mailAccount) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(smtpServer) || port == 0) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "发件邮箱信息无效"; return Json(resp); } else { var smtpDB = mailData.MailSendSmtp.FirstOrDefault(x => x.Account == mailAccount); if (smtpDB == null) { smtpDB = new MailSendSmtp(); smtpDB.GID = Guid.NewGuid().ToString(); smtpDB.Account = mailAccount; mailData.MailSendSmtp.Add(smtpDB); } smtpDB.Password = password; smtpDB.Port = port; smtpDB.Server = smtpServer; smtpDB.UseSSL = smtpSSL; mailData.SaveChanges(); smtpConfig = smtpDB.GID; smtpInfo = smtpDB; } } else { if (!smtpList.Contains(smtpConfig)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "SmtpConfig无效"; return Json(resp); } smtpInfo = mailData.MailSendSmtp.AsNoTracking().First(x => x.GID == smtpConfig); } for (var i = 0; i < Request.Files.Count; i++) { var file = Request.Files[i]; if (file.ContentLength == 0) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "文件内容不能为空"; return Json(resp); } var fileExt = Path.GetExtension(file.FileName).ToLower(); if (fileExt != ".eml") { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "文件格式错误"; return Json(resp); } MimeMessage mail = null; try { mail = MimeMessage.Load(file.InputStream); } catch { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "文件格式错误"; return Json(resp); } if (string.IsNullOrEmpty(mail.Subject)) { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "主题不能为空"; return Json(resp); } //发送人 mail.From.Clear(); mail.From.Add(new MailboxAddress(string.IsNullOrEmpty(showName) ? smtpInfo.Account : showName, smtpInfo.Account)); //收件人 mail.To.Clear(); mail.To.AddRange(sendToList); //cc mail.Cc.Clear(); mail.Cc.AddRange(ccList); #region 附加前缀 var insTextList = new List(); if (!string.IsNullOrEmpty(prefixText)) { insTextList.AddRange(prefixText.Split("\r\n".ToCharArray())); } if (insTextList.Count > 0) { var textParts = mail.BodyParts.Where(x => !x.IsAttachment && x.ContentType.MediaType == "text"); if (textParts != null && textParts.Any()) { foreach (var part in textParts) { var tp = part as TextPart; var contentText = tp.Text; Encoding srcEncoding = null; if (!string.IsNullOrEmpty(tp.ContentType.Charset)) { srcEncoding = Encoding.GetEncoding(tp.ContentType.Charset); } if (tp.ContentType.MimeType.Contains("html")) { HtmlDocument htmldoc = new HtmlDocument(); var htmlEncoding = htmldoc.DetectEncodingHtml(tp.Text); htmldoc.LoadHtml(tp.Text); var bodyNode = htmldoc.DocumentNode.SelectSingleNode("//body"); var metaLastNode = htmldoc.DocumentNode.SelectSingleNode("//meta[last()]"); var metaCharsetNode = htmldoc.DocumentNode.SelectSingleNode("//meta[contains(@http-equiv,'Content-Type')]"); Encoding toEncoding = null; if (srcEncoding == null) { srcEncoding = htmldoc.DeclaredEncoding; } if (srcEncoding == Encoding.ASCII || srcEncoding == Encoding.GetEncoding("iso-8859-1") || srcEncoding == null) { toEncoding = Encoding.UTF8; tp.ContentType.CharsetEncoding = toEncoding; if (metaCharsetNode != null) { metaCharsetNode.SetAttributeValue("content", "text/html; charset=utf-8"); } } else { toEncoding = srcEncoding; } if (bodyNode != null) { var insTextReverse = new List(insTextList.ToArray()); insTextReverse.Reverse(); foreach (var text in insTextReverse) { var insNode = htmldoc.CreateElement("p"); insNode.AppendChild(htmldoc.CreateTextNode(text)); bodyNode.InsertBefore(insNode, bodyNode.FirstChild); } MemoryStream ms = new MemoryStream(); htmldoc.Save(ms, toEncoding); contentText = toEncoding.GetString(ms.ToArray()); } else if (metaLastNode != null) { foreach (var text in insTextList) { var insNode = htmldoc.CreateElement("p"); insNode.AppendChild(htmldoc.CreateTextNode(text)); htmldoc.DocumentNode.InsertAfter(insNode, metaLastNode); } MemoryStream ms = new MemoryStream(); htmldoc.Save(ms, toEncoding); contentText = toEncoding.GetString(ms.ToArray()); } else { contentText = $"

{string.Join("

", insTextList)}

{contentText}"; } tp.SetText(toEncoding, contentText); } else { Encoding toEncoding = null; if (srcEncoding == Encoding.ASCII || srcEncoding == Encoding.GetEncoding("iso-8859-1") || srcEncoding == null) { toEncoding = Encoding.UTF8; tp.ContentType.CharsetEncoding = toEncoding; } else { toEncoding = srcEncoding; } contentText = $"{string.Join("\r\n", insTextList)}\r\n{contentText}"; tp.SetText(toEncoding, contentText); tp.ContentType.CharsetEncoding = toEncoding; } } } else { var mult = new Multipart("mixed"); var newPart = new TextPart("plain"); newPart.SetText(Encoding.UTF8, string.Join("\r\n", insTextList)); mult.Add(newPart); mult.Add(mail.Body); mail.Body = mult; } } #endregion var mailSend = new Common.MailSend(); mailSend.GID = Guid.NewGuid().ToString().Replace("-", ""); mailSend.SendTo = mail.To.ToString(); mailSend.CCTo = mail.Cc?.ToString(); mailSend.Title = mail.Subject; mailSend.SmtpConfig = smtpConfig; mailData.MailSend.Add(mailSend); //保存eml文件 string emlFilePath = ConfigurationManager.AppSettings["EmlFilePath"]; var saveEmlPath = string.Empty; if (string.IsNullOrEmpty(emlFilePath)) { saveEmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "EmlFiles"); } else { saveEmlPath = Path.Combine(emlFilePath, "EmlFiles"); } if (!Directory.Exists(saveEmlPath)) { Directory.CreateDirectory(saveEmlPath); } var saveFileName = Path.Combine(saveEmlPath, $"{mailSend.GID}.eml"); mail.WriteTo(saveFileName); mailSend.EmlFile = saveFileName; //记录eml文件位置 } mailData.SaveChanges(); resp.Success = true; resp.Code = RespCommon.RespCodeSuccess; resp.Message = "提交成功"; return Json(resp); } else { resp.Success = false; resp.Code = RespCommon.RespCodeParamError; resp.Message = "请上传eml文件"; return Json(resp); } } #region 发送记录查询 //发件记录 [HttpGet] public ActionResult SendList() { return View(); } [HttpGet] public ActionResult SendListData(ReqMailSendList req, int offset = 1, int limit = 10, string sort = "", string order = "asc") { RespPage resp = new RespPage(); var query = from m in mailData.MailSend join s in mailData.MailSendSmtp on m.SmtpConfig equals s.GID select new RespMailSend { GID = m.GID, SendTo = m.SendTo, CCTo = m.CCTo, Title = m.Title, SendStatus = m.SendStatus, TryCount = m.TryCount, SendTime = m.SendTime, CreateTime = m.CreateTime, ShowName = m.ShowName, Sender = s.Account, AttachFiles = m.AttachFiles, CustomerSend = m.CustomerSend, ResultText = m.ResultText, }; #region 查询条件 if (!string.IsNullOrEmpty(req.SendTo)) { query = query.Where(x => x.SendTo.Contains(req.SendTo)); } if (!string.IsNullOrEmpty(req.Title)) { query = query.Where(x => x.Title.Contains(req.Title)); } if (!string.IsNullOrEmpty(req.SendStatus)) { query = query.Where(x => x.SendStatus == req.SendStatus); } if (!string.IsNullOrEmpty(req.Sender)) { query = query.Where(x => x.Sender.Contains(req.Sender)); } if (req.CreateTimeBegin.HasValue) { query = query.Where(x => x.CreateTime > req.CreateTimeBegin); } if (req.CreateTimeEnd.HasValue) { var dt = req.CreateTimeEnd.Value.AddDays(1); query = query.Where(x => x.CreateTime < dt); } if (req.SendTimeBegin.HasValue) { query = query.Where(x => x.SendTime > req.SendTimeBegin); } if (req.SendTimeEnd.HasValue) { var dt = req.SendTimeEnd.Value.AddDays(1); query = query.Where(x => x.SendTime < dt); } #endregion resp.Success = true; resp.Message = "查询成功"; resp.Total = query.Count(); resp.Limit = limit; resp.Offset = offset; resp.DataList = query.OrderByDescending(x => x.CreateTime).Skip(offset).Take(limit).ToList(); return Json(resp, JsonRequestBehavior.AllowGet); } [HttpGet] public ActionResult DownloadEml(string gid) { var mail = mailData.MailSend.AsNoTracking().FirstOrDefault(x => x.GID == gid); if (mail == null) { return Content("发送记录不存在"); } if (System.IO.File.Exists(mail.EmlFile)) { return File(mail.EmlFile, "application/octet-stream", $"{mail.Title}.eml"); } else { return Content("EML文件丢失"); } } #endregion [HttpGet] public ActionResult Test() { logger.Debug($"{DateTime.Now}"); return Content("ok"); } } }