MailSender.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. using MailKit;
  2. using MailKit.Net.Smtp;
  3. using MailKit.Security;
  4. using Microsoft.Extensions.Logging;
  5. using Microsoft.Extensions.Options;
  6. using MimeKit;
  7. using MTWorkHR.Core.Email;
  8. using MTWorkHR.Core.Entities;
  9. using MTWorkHR.Core.Global;
  10. //using SendGrid;
  11. //using SendGrid.Helpers.Mail;
  12. using System;
  13. using System.Collections.Generic;
  14. using System.Linq;
  15. using System.Net;
  16. using System.Text;
  17. using System.Threading.Tasks;
  18. namespace MTWorkHR.Infrastructure.EmailService
  19. {
  20. public class MailSender : IMailSender
  21. {
  22. public AppSettingsConfiguration _configuration { get; }
  23. private readonly ILogger<MailSender> _logger;
  24. public MailSender(AppSettingsConfiguration configuration,ILogger<MailSender> logger)
  25. {
  26. _configuration = configuration;
  27. _logger = logger;
  28. }
  29. //public async Task<bool> SendEmailGrid(EmailMessage email)
  30. //{
  31. // var client = new SendGridClient(_configuration.MailSettings.ApiKey);
  32. // var to = new EmailAddress(email.To);
  33. // var from = new EmailAddress
  34. // {
  35. // Email = _configuration.MailSettings.FromAddress,
  36. // Name = _configuration.MailSettings.FromName
  37. // };
  38. // email.Body = email.Body + " /n" + email.url;
  39. // var message = MailHelper.CreateSingleEmail(from, to, email.Subject, email.Body, email.Body);
  40. // var response = await client.SendEmailAsync(message);
  41. // return response.IsSuccessStatusCode;
  42. //}
  43. /// <summary>
  44. /// Send Email using an azure email address, smtpUserName and password
  45. /// </summary>
  46. /// <param name="email"></param>
  47. /// <returns></returns>
  48. public async Task<bool> SendEmailAzure(EmailMessage email)
  49. {
  50. var mailTo = email.To;
  51. var settings = _configuration.MailSettings;
  52. // Validate configuration settings
  53. if (string.IsNullOrEmpty(settings.SmtpUsername) ||
  54. string.IsNullOrEmpty(settings.Password) ||
  55. string.IsNullOrEmpty(settings.FromAddress) ||
  56. string.IsNullOrEmpty(settings.Host) ||
  57. settings.Port == 0 ||
  58. string.IsNullOrEmpty(mailTo))
  59. {
  60. Console.WriteLine("Invalid mail settings or recipient address.");
  61. return false;
  62. }
  63. // Create the email message
  64. var emailObj = new MimeMessage();
  65. emailObj.From.Add(new MailboxAddress("MTWork", settings.FromAddress)); // Display name + verified sender address
  66. emailObj.Sender = MailboxAddress.Parse(settings.FromAddress);
  67. emailObj.To.Add(MailboxAddress.Parse(mailTo));
  68. emailObj.Subject = email.Subject;
  69. // Build the email body with HTML content
  70. var builder = new BodyBuilder();
  71. var emailBody = email.Body;
  72. if (!string.IsNullOrEmpty(email.url))
  73. {
  74. // Use string interpolation with proper HTML escaping
  75. emailBody += $"Please click on <a href=\"{email.url}\">this link</a> or copy the following URL and open it: '{WebUtility.HtmlEncode(email.url)}'";
  76. }
  77. builder.HtmlBody = emailBody;
  78. emailObj.Body = builder.ToMessageBody();
  79. // Initialize SMTP client with logging
  80. //using var logger = new ProtocolLogger("smtp.log", append: false);
  81. using var smtp = new SmtpClient();
  82. try
  83. {
  84. _logger.LogInformation("Connecting to SMTP server {Host}:{Port} with username :[{SmtpUsername}]",
  85. settings.Host, settings.Port, settings.SmtpUsername);
  86. // Connect to Azure Communication Services SMTP server
  87. await smtp.ConnectAsync(settings.Host, settings.Port, SecureSocketOptions.StartTls);
  88. _logger.LogInformation("Authenticating with username {SmtpUsername}:[{pass}]", settings.SmtpUsername, settings.Password);
  89. // Authenticate using SMTP Username and Entra app client secret
  90. await smtp.AuthenticateAsync(settings.SmtpUsername, settings.Password);
  91. _logger.LogInformation("Sending email to {To} with subject {Subject}", mailTo, email.Subject);
  92. // Send the email
  93. await smtp.SendAsync(emailObj);
  94. _logger.LogInformation("Email sent successfully to {To}", mailTo);
  95. }
  96. catch (AuthenticationException ex)
  97. {
  98. _logger.LogError(ex, "Authentication failed for username {SmtpUsername}: {Message}",
  99. settings.SmtpUsername, ex.Message);
  100. throw ex;
  101. }
  102. catch (SmtpCommandException ex)
  103. {
  104. _logger.LogError(ex, "SMTP error: {Message}, Status: {StatusCode}", ex.Message, ex.StatusCode);
  105. throw ex;
  106. }
  107. catch (Exception ex)
  108. {
  109. _logger.LogError(ex, "Error sending email to {To}: {Message}", mailTo, ex.Message);
  110. throw ex;
  111. }
  112. finally
  113. {
  114. // Ensure disconnection
  115. if (smtp.IsConnected)
  116. {
  117. await smtp.DisconnectAsync(true);
  118. _logger.LogInformation("Disconnected from SMTP server");
  119. }
  120. }
  121. return true;
  122. }
  123. //Send Email using an email address and password
  124. public async Task<bool> SendEmail(EmailMessage email)
  125. {
  126. var mailTo = email.To;
  127. var settings = _configuration.MailSettings;
  128. if (settings.FromAddress == null || settings.FromAddress== "" || settings.Password == null || settings.TemplatePath == null || settings.Host == null || settings.Port == 0 || mailTo == null)
  129. return false;
  130. //string FilePath = _configuration.MailSettings.TemplatePath;
  131. //StreamReader str = new StreamReader(FilePath);
  132. //string MailText = str.ReadToEnd();
  133. //str.Close();
  134. //MailText = MailText.Replace("[NotificationType]", email.Subject).Replace("[Info]", email.Body).Replace("[Link]", email.url );
  135. var emailObj = new MimeMessage();
  136. emailObj.From.Add(new MailboxAddress("MTWork", _configuration.MailSettings.FromAddress)); // Ensure From matches authenticated email
  137. emailObj.Sender = MailboxAddress.Parse(_configuration.MailSettings.FromAddress);
  138. emailObj.To.Add(MailboxAddress.Parse(mailTo));
  139. emailObj.Subject = email.Subject;
  140. var builder = new BodyBuilder();
  141. email.Body = email.Body +
  142. ( string.IsNullOrEmpty( email.url ) ?"": "Please click on <a href =\"" + email.url + "\">this link</a> or copy the following url and open it : \' " + email.url+"\'");
  143. builder.HtmlBody = email.Body;
  144. emailObj.Body = builder.ToMessageBody();
  145. using var logger = new ProtocolLogger("smtp.log", append: false);
  146. using var smtp = new SmtpClient(logger);
  147. try
  148. {
  149. await smtp.ConnectAsync(_configuration.MailSettings.Host, _configuration.MailSettings.Port, MailKit.Security.SecureSocketOptions.StartTls);
  150. await smtp.AuthenticateAsync(_configuration.MailSettings.FromAddress, _configuration.MailSettings.Password);
  151. _logger.LogInformation("Sending email to {To} with subject {Subject}", mailTo, email.Subject);
  152. await smtp.SendAsync(emailObj);
  153. _logger.LogInformation("Email sent successfully to {To}", mailTo);
  154. }
  155. catch (AuthenticationException ex)
  156. {
  157. _logger.LogError(ex, "Authentication failed for username {SmtpUsername}: {Message}",
  158. settings.SmtpUsername, ex.Message);
  159. throw ex;
  160. }
  161. catch (SmtpCommandException ex)
  162. {
  163. _logger.LogError(ex, "SMTP error: {Message}, Status: {StatusCode}", ex.Message, ex.StatusCode);
  164. throw ex;
  165. }
  166. catch (Exception ex)
  167. {
  168. _logger.LogError(ex, "Error sending email to {To}: {Message}", mailTo, ex.Message);
  169. throw ex;
  170. }
  171. finally
  172. {
  173. _logger.LogInformation("Disconnected from SMTP server");
  174. await smtp.DisconnectAsync(true);
  175. }
  176. return true;
  177. }
  178. }
  179. }