using System.Text; using DigitalData.MessagingService.Application.Common.Interfaces; using DigitalData.MessagingService.Domain.Exceptions; using Limilabs.Client.SMTP; using Limilabs.Mail; using Limilabs.Mail.Headers; using DigitalData.MessagingService.Infrastructure.Services.Extensions; using DigitalData.MessagingService.Abstraction; namespace DigitalData.MessagingService.Infrastructure.Services; /// /// Email service using Limilabs Mail.dll for SMTP operations (send-only). /// Commercial-grade library with superior Exchange support. /// SMTP configuration is injected via IOptions<EmailAccountsOptions> from appsettings.json. /// Uses the first account in the list whose equals "default", /// or falls back to the first account if none is named "default". /// public class LimilabsEmailService( IEncryptionService encryptionService) : IEmailService { // Register encoding provider for Limilabs (requires windows-1252 and other code pages) static LimilabsEmailService() { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); } public async Task SendEmailAsync(EmailAccountDto from, IEnumerable recipients, string subject, string body, bool isHtml = true, CancellationToken cancellationToken = default) { using var smtp = new Smtp(); try { await ConnectAndAuthenticateSmtpAsync(smtp, from); var builder = new MailBuilder(); builder.From.Add(new MailBox(from.Username)); foreach (var recipient in recipients) builder.To.Add(new MailBox(recipient)); builder.Subject = subject; if (isHtml) { builder.Html = body; } else { builder.Text = body; } var mail = builder.Create(); var result = await smtp.SendMessageAsync(mail, cancellationToken); if (result.Status != SendMessageStatus.Success) { throw new InvalidOperationException($"Failed to send email. Status: {result.Status}"); } await smtp.CloseAsync(cancellationToken); await Task.CompletedTask; // For async consistency } catch (Limilabs.Client.ServerException ex) { await smtp.CloseSafelyAsync(); throw new AuthenticationFailedException("SMTP authentication failed. Check credentials or OAuth2 configuration.", ex); } catch (Exception ex) { await smtp.CloseSafelyAsync(); throw new InvalidOperationException("Failed to send email via SMTP server.", ex); } } private async Task ConnectAndAuthenticateSmtpAsync(Smtp smtp, EmailAccountDto smtpAccount) { if (smtpAccount.SmtpUseSsl) { await smtp.ConnectSSLAsync(smtpAccount.SmtpServer, smtpAccount.SmtpPort); } else { await smtp.ConnectAsync(smtpAccount.SmtpServer, smtpAccount.SmtpPort); } if (smtpAccount.UseOAuth2) { throw new NotSupportedException("OAuth2 is not configured for this SMTP account. UseOAuth2 must be false."); } else { var password = smtpAccount.PasswordEncrypted ? encryptionService.Decrypt(smtpAccount.Password) : smtpAccount.Password; await smtp.LoginAsync(smtpAccount.Username, password); } } }