Skip to content

Commit

Permalink
NLog 5 compatible, all properties layoutable, Update MailKit (#137)
Browse files Browse the repository at this point in the history
* NLog 5 compatible, Update MailKit

* run tests with .NET 6 as .NET 5 is EOL

* obsolete fix and cleanup

* vm

* layoutable

* fix typos

* update Microsoft.SourceLink.GitHub

* icon and license

* readme

* replace with RenderLogEvent

Co-authored-by: Julian Verdurmen <304NotModified@users.noreply.github.com>
  • Loading branch information
304NotModified and 304NotModified authored May 22, 2022
1 parent 195ebe3 commit d79ad14
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 101 deletions.
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=nlog.mailkit&metric=bugs)](https://sonarcloud.io/summary/new_code?id=nlog.mailkit)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=nlog.mailkit&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=nlog.mailkit)


Alternative Mail target for [NLog](https://github.com/nlog/nlog) using MailKit. Compatible with .NET standard 2+
Alternative Mail target for [NLog](https://github.com/nlog/nlog) using [MailKit](https://github.com/jstedfast/MailKit). Compatible with .NET standard 2+

Including this package will replace the original mail target and has the
same options as the original mail target, see [docs of the original mailTarget](https://github.com/NLog/NLog/wiki/Mail-Target)
Expand All @@ -26,7 +25,7 @@ This library is integration tested with the [SmtpServer NuGet package](https://w
`Install-Package NLog.MailKit` or in your csproj:

```xml
<PackageReference Include="NLog.MailKit" Version="4.*" />
<PackageReference Include="NLog.MailKit" Version="5.*" />
```

2) Add to your nlog.config:
Expand All @@ -43,9 +42,6 @@ and config options can be found here: https://github.com/NLog/NLog/wiki/Mail-Tar
Use `skipCertificateValidation="true"` for prevent `AuthenticationException` if your remote certificate for smtpServer is invalid - not recommend!





### License
BSD. License of MailKit is MIT

8 changes: 4 additions & 4 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ trigger:
- master
pool:
name: Azure Pipelines
vmImage: 'windows-2019'
vmImage: 'windows-latest'
demands:
- msbuild
- visualstudio
Expand All @@ -13,14 +13,14 @@ variables:
Solution: 'src/NLog.MailKit.sln'
BuildPlatform: 'Any CPU'
BuildConfiguration: 'Release'
Version: '4.1.0'
Version: '5.0.0'
FullVersion: '$(Version).$(Build.BuildId)'

steps:
- task: NuGetToolInstaller@0
displayName: 'Use NuGet 5.x'
displayName: 'Use NuGet 6.x'
inputs:
versionSpec: 5.x
versionSpec: 6.x

- task: DotNetCoreCLI@2
displayName: 'dotnet restore'
Expand Down
6 changes: 6 additions & 0 deletions src/NLog.MailKit.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=longdate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=machinename/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NTLM/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=stringbuilder/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=XOAUTH/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
97 changes: 55 additions & 42 deletions src/NLog.MailKit/MailTarget.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2004-2021 Jaroslaw Kowalski <jaak@jkowalski.net>, Kim Christensen, Julian Verdurmen
// Copyright (c) 2004-2022 Jaroslaw Kowalski <jaak@jkowalski.net>, Kim Christensen, Julian Verdurmen
//
// All rights reserved.
//
Expand Down Expand Up @@ -34,6 +34,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using MailKit.Net.Smtp;
using MailKit.Security;
Expand Down Expand Up @@ -80,8 +81,12 @@ namespace NLog.MailKit
/// <code lang="C#" source="examples/targets/Configuration API/Mail/Buffered/Example.cs" />
/// </example>
[Target("Mail")]
[SuppressMessage("ReSharper", "RedundantStringFormatCall")]
public class MailTarget : TargetWithLayoutHeaderAndFooter
{
private static readonly Encoding DefaultEncoding = System.Text.Encoding.UTF8;
private const SecureSocketOptions DefaultSecureSocketOption = SecureSocketOptions.StartTlsWhenAvailable;

private const string RequiredPropertyIsEmptyFormat = "After the processing of the MailTarget's '{0}' property it appears to be empty. The email message will not be sent.";

/// <summary>
Expand All @@ -94,10 +99,10 @@ public MailTarget()
{
Body = "${message}${newline}";
Subject = "Message from NLog on ${machinename}";
Encoding = Encoding.UTF8;
Encoding = DefaultEncoding;
SmtpPort = 25;
SmtpAuthentication = SmtpAuthenticationMode.None;
SecureSocketOption = SecureSocketOptions.StartTlsWhenAvailable;
SecureSocketOption = DefaultSecureSocketOption;
Timeout = 10000;
}

Expand Down Expand Up @@ -143,7 +148,8 @@ public MailTarget(string name) : this()
/// </summary>
/// <value>A value of <c>true</c> if new lines should be added; otherwise, <c>false</c>.</value>
/// <docgen category='Layout Options' order='99' />
public bool AddNewLines { get; set; }
[DefaultValue(false)]
public Layout<bool> AddNewLines { get; set; }

/// <summary>
/// Gets or sets the mail subject.
Expand All @@ -170,14 +176,14 @@ public Layout Body
/// </summary>
/// <docgen category='Layout Options' order='20' />
[DefaultValue("UTF8")]
public Encoding Encoding { get; set; }
public Layout<Encoding> Encoding { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to send message as HTML instead of plain text.
/// </summary>
/// <docgen category='Layout Options' order='11' />
[DefaultValue(false)]
public bool Html { get; set; }
public Layout<bool> Html { get; set; }

/// <summary>
/// Gets or sets SMTP Server to be used for sending.
Expand All @@ -190,16 +196,16 @@ public Layout Body
/// </summary>
/// <docgen category='SMTP Options' order='11' />
[DefaultValue("None")]
public SmtpAuthenticationMode SmtpAuthentication { get; set; }
public Layout<SmtpAuthenticationMode> SmtpAuthentication { get; set; }

/// <summary>
/// Gets or sets the username used to connect to SMTP server (used when SmtpAuthentication is set to "basic").
/// Gets or sets the username used to connect to SMTP server (used when <see cref="SmtpAuthentication"/> is set to "basic").
/// </summary>
/// <docgen category='SMTP Options' order='12' />
public Layout SmtpUserName { get; set; }

/// <summary>
/// Gets or sets the password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic").
/// Gets or sets the password used to authenticate against SMTP server (used when <see cref="SmtpAuthentication"/> is set to "basic").
/// </summary>
/// <docgen category='SMTP Options' order='13' />
public Layout SmtpPassword { get; set; }
Expand All @@ -212,31 +218,31 @@ public Layout Body
/// <docgen category='SMTP Options' order='14' />
/// .
[DefaultValue(false)]
public bool EnableSsl { get; set; }
public Layout<bool> EnableSsl { get; set; }

/// <summary>
/// Provides a way of specifying the SSL and/or TLS encryption
///
/// If <see cref="EnableSsl" /> is <c>true</c>, then <see cref="SecureSocketOptions.SslOnConnect" /> will be used.
/// </summary>
[DefaultValue(SecureSocketOptions.StartTlsWhenAvailable)]
[DefaultValue(DefaultSecureSocketOption)]
[CLSCompliant(false)]
public SecureSocketOptions SecureSocketOption { get; set; }
public Layout<SecureSocketOptions> SecureSocketOption { get; set; }

/// <summary>
/// Gets or sets the port number that SMTP Server is listening on.
/// </summary>
/// <docgen category='SMTP Options' order='15' />
[DefaultValue(25)]
public int SmtpPort { get; set; }
public Layout<int> SmtpPort { get; set; }

/// <summary>
/// Gets or sets a value indicating whether SmtpClient should ignore invalid certificate.
/// </summary>
/// <docgen category='SMTP Options' order='16' />
/// .
[DefaultValue(false)]
public bool SkipCertificateValidation { get; set; }
public Layout<bool> SkipCertificateValidation { get; set; }

/// <summary>
/// Gets or sets the priority used for sending mails.
Expand All @@ -248,22 +254,22 @@ public Layout Body
/// </summary>
/// <remarks>Only happens when <see cref="Html" /> is set to true.</remarks>
[DefaultValue(false)]
public bool ReplaceNewlineWithBrTagInHtml { get; set; }
public Layout<bool> ReplaceNewlineWithBrTagInHtml { get; set; }

/// <summary>
/// Gets or sets a value indicating the SMTP client timeout.
/// </summary>
/// <remarks>Warning: zero is not infinit waiting</remarks>
/// <remarks>Warning: zero is not infinite waiting</remarks>
[DefaultValue(10000)]
public int Timeout { get; set; }
public Layout<int> Timeout { get; set; }

/// <summary>
/// Renders the logging event message and adds it to the internal ArrayList of log messages.
/// </summary>
/// <param name="logEvent">The logging event.</param>
protected override void Write(AsyncLogEventInfo logEvent)
{
Write((IList<AsyncLogEventInfo>)new[] { logEvent });
Write(new[] { logEvent });
}

/// <summary>
Expand All @@ -289,9 +295,10 @@ protected override void InitializeTarget()
InternalLogger.Debug("Init mailtarget with mailkit");
CheckRequiredParameters();

if (SmtpAuthentication == SmtpAuthenticationMode.Ntlm)
var smtpAuthentication = RenderLogEvent(SmtpAuthentication, LogEventInfo.CreateNullEvent());
if (smtpAuthentication == SmtpAuthenticationMode.Ntlm)
{
throw new NLogConfigurationException("Ntlm not yet supported");
throw new NLogConfigurationException("NTLM not yet supported");
}

base.InitializeTarget();
Expand Down Expand Up @@ -321,26 +328,29 @@ private void ProcessSingleMailMessage(IList<AsyncLogEventInfo> events)
using (var client = new SmtpClient())
{
CheckRequiredParameters();
client.Timeout = Timeout;
client.Timeout = RenderLogEvent(Timeout, lastEvent);

var renderedHost = SmtpServer.Render(lastEvent);
if (string.IsNullOrEmpty(renderedHost))
{
throw new NLogRuntimeException(RequiredPropertyIsEmptyFormat, nameof(SmtpServer));
throw new NLogRuntimeException(string.Format(RequiredPropertyIsEmptyFormat, nameof(SmtpServer)));
}

var secureSocketOptions = EnableSsl ? SecureSocketOptions.SslOnConnect : SecureSocketOption;
InternalLogger.Debug("Sending mail to {0} using {1}:{2} (socket option={3})", message.To, renderedHost, SmtpPort, secureSocketOptions);
var enableSsl = RenderLogEvent(EnableSsl, lastEvent);
var secureSocketOptions = enableSsl ? SecureSocketOptions.SslOnConnect : RenderLogEvent(SecureSocketOption, lastEvent, DefaultSecureSocketOption);
var smtpPort = RenderLogEvent(SmtpPort, lastEvent);
InternalLogger.Debug("Sending mail to {0} using {1}:{2} (socket option={3})", message.To, renderedHost, smtpPort, secureSocketOptions);
InternalLogger.Trace(" Subject: '{0}'", message.Subject);
InternalLogger.Trace(" From: '{0}'", message.From);

if (SkipCertificateValidation)
var skipCertificateValidation = RenderLogEvent(SkipCertificateValidation, lastEvent);
if (skipCertificateValidation)
{
client.ServerCertificateValidationCallback += (s, cert, chain, sslPolicyErrors) => true;
}


client.Connect(renderedHost, SmtpPort, secureSocketOptions);
client.Connect(renderedHost, smtpPort, secureSocketOptions);
InternalLogger.Trace(" Connecting succesfull");

// Note: since we don't have an OAuth2 token, disable
Expand All @@ -349,7 +359,8 @@ private void ProcessSingleMailMessage(IList<AsyncLogEventInfo> events)

// Note: only needed if the SMTP server requires authentication

if (SmtpAuthentication == SmtpAuthenticationMode.Basic)
var smtpAuthentication = RenderLogEvent(SmtpAuthentication, LogEventInfo.CreateNullEvent());
if (smtpAuthentication == SmtpAuthenticationMode.Basic)
{
var userName = SmtpUserName?.Render(lastEvent);
var password = SmtpPassword?.Render(lastEvent);
Expand Down Expand Up @@ -397,10 +408,11 @@ private void ProcessSingleMailMessage(IList<AsyncLogEventInfo> events)
private StringBuilder CreateBodyBuffer(IEnumerable<AsyncLogEventInfo> events, LogEventInfo firstEvent, LogEventInfo lastEvent)
{
var bodyBuffer = new StringBuilder();
var addNewLines = RenderLogEvent(AddNewLines, firstEvent, false);
if (Header != null)
{
bodyBuffer.Append(Header.Render(firstEvent));
if (AddNewLines)
if (addNewLines)
{
bodyBuffer.Append("\n");
}
Expand All @@ -409,7 +421,7 @@ private StringBuilder CreateBodyBuffer(IEnumerable<AsyncLogEventInfo> events, Lo
foreach (var eventInfo in events)
{
bodyBuffer.Append(Layout.Render(eventInfo.LogEvent));
if (AddNewLines)
if (addNewLines)
{
bodyBuffer.Append("\n");
}
Expand All @@ -418,7 +430,7 @@ private StringBuilder CreateBodyBuffer(IEnumerable<AsyncLogEventInfo> events, Lo
if (Footer != null)
{
bodyBuffer.Append(Footer.Render(lastEvent));
if (AddNewLines)
if (addNewLines)
{
bodyBuffer.Append("\n");
}
Expand All @@ -431,17 +443,17 @@ private void CheckRequiredParameters()
{
if (SmtpServer == null)
{
throw new NLogConfigurationException(RequiredPropertyIsEmptyFormat, nameof(SmtpServer));
throw new NLogConfigurationException(string.Format(RequiredPropertyIsEmptyFormat, nameof(SmtpServer)));
}

if (From == null)
{
throw new NLogConfigurationException(RequiredPropertyIsEmptyFormat, nameof(From));
throw new NLogConfigurationException(string.Format(RequiredPropertyIsEmptyFormat, nameof(From)));
}
}

/// <summary>
/// Create key for grouping. Needed for multiple events in one mailmessage
/// Create key for grouping. Needed for multiple events in one mail message
/// </summary>
/// <param name="logEvent">event for rendering layouts </param>
/// <returns>string to group on</returns>
Expand All @@ -456,7 +468,7 @@ private string GetSmtpSettingsKey(LogEventInfo logEvent)
AppendLayout(sb, logEvent, SmtpServer);
AppendLayout(sb, logEvent, SmtpPassword);
AppendLayout(sb, logEvent, SmtpUserName);

return sb.ToString();
}

Expand All @@ -476,7 +488,7 @@ private static void AppendLayout(StringBuilder sb, LogEventInfo logEvent, Layout
}

/// <summary>
/// Create the mailmessage with the addresses, properties and body.
/// Create the mail message with the addresses, properties and body.
/// </summary>
private MimeMessage CreateMailMessage(LogEventInfo lastEvent, string body)
{
Expand All @@ -486,7 +498,7 @@ private MimeMessage CreateMailMessage(LogEventInfo lastEvent, string body)

if (string.IsNullOrEmpty(renderedFrom))
{
throw new NLogRuntimeException(RequiredPropertyIsEmptyFormat, "From");
throw new NLogRuntimeException(string.Format(RequiredPropertyIsEmptyFormat, "From"));
}

msg.From.Add(MailboxAddress.Parse(renderedFrom));
Expand All @@ -497,13 +509,11 @@ private MimeMessage CreateMailMessage(LogEventInfo lastEvent, string body)

if (!addedTo && !addedCc && !addedBcc)
{
throw new NLogRuntimeException(RequiredPropertyIsEmptyFormat, "To/Cc/Bcc");
throw new NLogRuntimeException(string.Format(RequiredPropertyIsEmptyFormat, "To/Cc/Bcc"));
}

msg.Subject = Subject == null ? string.Empty : Subject.Render(lastEvent).Trim();

//todo msg.BodyEncoding = Encoding;

if (Priority != null)
{
var renderedPriority = Priority.Render(lastEvent);
Expand All @@ -513,15 +523,18 @@ private MimeMessage CreateMailMessage(LogEventInfo lastEvent, string body)
TextPart CreateBodyPart()
{
var newBody = body;
if (Html && ReplaceNewlineWithBrTagInHtml)
var html = RenderLogEvent(Html, lastEvent);
var replaceNewlineWithBrTagInHtml = RenderLogEvent(ReplaceNewlineWithBrTagInHtml, lastEvent);
if (html && replaceNewlineWithBrTagInHtml)
{
newBody = newBody?.Replace(Environment.NewLine, "<br/>");
}

return new TextPart(Html ? TextFormat.Html : TextFormat.Plain)
var encoding = RenderLogEvent(Encoding, lastEvent, DefaultEncoding);
return new TextPart(html ? TextFormat.Html : TextFormat.Plain)
{
Text = newBody,
ContentType = { Charset = Encoding?.WebName }
ContentType = { Charset = encoding?.WebName }
};
}

Expand Down
Binary file added src/NLog.MailKit/N.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit d79ad14

Please sign in to comment.