public async Task SendAsync(EmailConfig.OutgoingConfig config, IMailMessage message,
                                    CancellationToken cancellationToken = default)
        {
            var mailMessage = message.ToMailKitMessage();

            await ConnectAsync(_client, config, cancellationToken);

            _log.LogInformation(Message.SendInfo, message, config);
            await Policy.Handle <SocketException>()
            .WaitAndRetryAsync(_retryConfig.Max, _ => _retryConfig.Delay,
                               (exception, ts) => _log.LogError(exception, $"Error sending mail, retrying in {ts}."))
            .ExecuteAsync(token => _client.SendAsync(mailMessage, token), cancellationToken);

            await _client.DisconnectAsync(true, cancellationToken);
        }
        public async Task WhenOneOfUsernameOrPasswordNotProvidedThenItWillConnectToServerAnonymously()
        {
            var client       = new Mock <ISmtpClient>();
            var bootstrapper = ConfigureServices(services => services.AddTransient(_ => client.Object));
            var sut          = bootstrapper.GetService <IMailSender>();
            var account      = new EmailConfig.OutgoingConfig {
                Username = "******", Password = string.Empty
            };
            var message = new MailMessage
            {
                Subject  = Guid.NewGuid().ToString("n"),
                HtmlBody = nameof(WhenBothUsernameAndPasswordProvidedThenItWillUseThemToAuthenticate),
                From     = account.Username,
                To       = { "random@domain" }
            };

            await sut.SendAsync(account, message);

            client.Verify(x => x.AuthenticateAsync(It.Is <string>(u => u == account.Username),
                                                   It.Is <string>(p => p == account.Password), It.IsAny <CancellationToken>()), Times.Never);
        }
        private async Task ConnectAsync(ISmtpClient client, EmailConfig.OutgoingConfig cfg,
                                        CancellationToken cancellationToken)
        {
            _log.LogDebug("Connecting to mail server using {@Config}", cfg);
            var socketOptions = client.ConfigureSecurity(cfg.UseSecureMode);
            await Policy
            .Handle <SocketException>().Or <System.IO.IOException>().Or <SslHandshakeException>()
            .WaitAndRetryAsync(_retryConfig.Max, _ => _retryConfig.Delay,
                               (exc, delay, attempt, _) => _log.LogError(exc, Message.ConnError, attempt, cfg, delay))
            .ExecuteAsync(token => client.ConnectAsync(cfg.ServerAddress, cfg.Port, socketOptions, token),
                          cancellationToken);

            if (!string.IsNullOrWhiteSpace(cfg.Username) && !string.IsNullOrWhiteSpace(cfg.Password))
            {
                await Policy.Handle <SocketException>()
                .WaitAndRetryAsync(_retryConfig.Max, _ => _retryConfig.Delay,
                                   (exc, delay, attempt, _) => _log.LogError(exc, Message.AuthError, attempt, cfg, delay))
                .ExecuteAsync(token => client.AuthenticateAsync(cfg.Username, cfg.Password, token),
                              cancellationToken);
            }
        }