Skip to content

Commit 8a25f6b

Browse files
authored
Added support for PickupDirectoryLocation (#178)
1 parent 5bf876b commit 8a25f6b

File tree

5 files changed

+130
-55
lines changed

5 files changed

+130
-55
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ same options as the original mail target, see [docs of the original mailTarget](
1212

1313
Currently not implemented:
1414

15-
- PickupDirectory
1615
- NTLM auth
1716

1817
This library is integration tested with the [SmtpServer NuGet package](https://www.nuget.org/packages/SmtpServer/)

azure-pipelines.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ variables:
1313
Solution: 'src/NLog.MailKit.sln'
1414
BuildPlatform: 'Any CPU'
1515
BuildConfiguration: 'Release'
16-
Version: '5.1.0'
16+
Version: '5.2.0'
1717
FullVersion: '$(Version).$(Build.BuildId)'
1818

1919
steps:

src/NLog.MailKit/MailTarget.cs

Lines changed: 100 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ public Layout Body
245245
/// <remarks>Warning: zero is not infinite waiting</remarks>
246246
public Layout<int> Timeout { get; set; } = 10000;
247247

248+
/// <summary>
249+
/// Gets or sets the folder where applications save mail messages to be processed by the local SMTP server.
250+
/// </summary>
251+
/// <docgen category='SMTP Options' order='17' />
252+
public Layout PickupDirectoryLocation { get; set; }
253+
248254
/// <summary>
249255
/// Gets the array of email headers that are transmitted with this email message
250256
/// </summary>
@@ -305,58 +311,20 @@ private void ProcessSingleMailMessage(IEnumerable<AsyncLogEventInfo> events)
305311

306312
var message = CreateMailMessage(lastEvent, bodyBuffer.ToString());
307313

308-
using (var client = new SmtpClient())
314+
var pickupFolder = RenderLogEvent(PickupDirectoryLocation, lastEvent);
315+
if (!string.IsNullOrEmpty(pickupFolder))
309316
{
310-
client.Timeout = RenderLogEvent(Timeout, lastEvent);
311-
312-
var renderedHost = RenderLogEvent(SmtpServer, lastEvent);
313-
if (string.IsNullOrEmpty(renderedHost))
314-
{
315-
throw new NLogRuntimeException(string.Format(RequiredPropertyIsEmptyFormat, nameof(SmtpServer)));
316-
}
317-
318-
var enableSsl = RenderLogEvent(EnableSsl, lastEvent);
319-
var secureSocketOptions = enableSsl ? SecureSocketOptions.SslOnConnect : RenderLogEvent(SecureSocketOption, lastEvent, DefaultSecureSocketOption);
320-
var smtpPort = RenderLogEvent(SmtpPort, lastEvent);
321-
InternalLogger.Debug("Sending mail to {0} using {1}:{2}", message.To, renderedHost, smtpPort);
322-
InternalLogger.Trace(" Subject: '{0}'", message.Subject);
323-
InternalLogger.Trace(" From: '{0}'", message.From);
324-
325-
var skipCertificateValidation = RenderLogEvent(SkipCertificateValidation, lastEvent);
326-
if (skipCertificateValidation)
327-
{
328-
client.ServerCertificateValidationCallback += (s, cert, chain, sslPolicyErrors) => true;
329-
}
330-
331-
332-
client.Connect(renderedHost, smtpPort, secureSocketOptions);
333-
InternalLogger.Trace("{0}: Connecting succesfull", this);
334-
335-
// Note: since we don't have an OAuth2 token, disable
336-
// the XOAUTH2 authentication mechanism.
337-
client.AuthenticationMechanisms.Remove("XOAUTH2");
338-
339-
// Note: only needed if the SMTP server requires authentication
340-
341-
var smtpAuthentication = RenderLogEvent(SmtpAuthentication, lastEvent);
342-
if (smtpAuthentication == SmtpAuthenticationMode.Basic)
343-
{
344-
var userName = RenderLogEvent(SmtpUserName, lastEvent);
345-
var password = RenderLogEvent(SmtpPassword, lastEvent);
346-
347-
InternalLogger.Trace("{0}: Authenticate with username '{1}'", this, userName);
348-
client.Authenticate(userName, password);
349-
}
350-
351-
client.Send(message);
352-
InternalLogger.Trace("{0}: Sending mail done. Disconnecting", this);
353-
client.Disconnect(true);
354-
InternalLogger.Trace("{0}: Disconnected", this);
355-
356-
foreach (var ev in events)
357-
{
358-
ev.Continuation(null);
359-
}
317+
var emailFilePath = ResolvePickupDirectoryLocationFilePath(pickupFolder);
318+
message.WriteTo(emailFilePath);
319+
}
320+
else
321+
{
322+
SendMailMessage(message, lastEvent);
323+
}
324+
325+
foreach (var ev in events)
326+
{
327+
ev.Continuation(null);
360328
}
361329
}
362330
catch (Exception exception)
@@ -374,6 +342,57 @@ private void ProcessSingleMailMessage(IEnumerable<AsyncLogEventInfo> events)
374342
}
375343
}
376344

345+
private void SendMailMessage(MimeMessage message, LogEventInfo lastEvent)
346+
{
347+
using (var client = new SmtpClient())
348+
{
349+
client.Timeout = RenderLogEvent(Timeout, lastEvent);
350+
351+
var renderedHost = RenderLogEvent(SmtpServer, lastEvent);
352+
if (string.IsNullOrEmpty(renderedHost))
353+
{
354+
throw new NLogRuntimeException(string.Format(RequiredPropertyIsEmptyFormat, nameof(SmtpServer)));
355+
}
356+
357+
var enableSsl = RenderLogEvent(EnableSsl, lastEvent);
358+
var secureSocketOptions = enableSsl ? SecureSocketOptions.SslOnConnect : RenderLogEvent(SecureSocketOption, lastEvent, DefaultSecureSocketOption);
359+
var smtpPort = RenderLogEvent(SmtpPort, lastEvent);
360+
InternalLogger.Debug("Sending mail to {0} using {1}:{2}", message.To, renderedHost, smtpPort);
361+
InternalLogger.Trace(" Subject: '{0}'", message.Subject);
362+
InternalLogger.Trace(" From: '{0}'", message.From);
363+
364+
var skipCertificateValidation = RenderLogEvent(SkipCertificateValidation, lastEvent);
365+
if (skipCertificateValidation)
366+
{
367+
client.ServerCertificateValidationCallback += (s, cert, chain, sslPolicyErrors) => true;
368+
}
369+
370+
client.Connect(renderedHost, smtpPort, secureSocketOptions);
371+
InternalLogger.Trace("{0}: Connecting succesfull", this);
372+
373+
// Note: since we don't have an OAuth2 token, disable
374+
// the XOAUTH2 authentication mechanism.
375+
client.AuthenticationMechanisms.Remove("XOAUTH2");
376+
377+
// Note: only needed if the SMTP server requires authentication
378+
379+
var smtpAuthentication = RenderLogEvent(SmtpAuthentication, lastEvent);
380+
if (smtpAuthentication == SmtpAuthenticationMode.Basic)
381+
{
382+
var userName = RenderLogEvent(SmtpUserName, lastEvent);
383+
var password = RenderLogEvent(SmtpPassword, lastEvent);
384+
385+
InternalLogger.Trace("{0}: Authenticate with username '{1}'", this, userName);
386+
client.Authenticate(userName, password);
387+
}
388+
389+
client.Send(message);
390+
InternalLogger.Trace("{0}: Sending mail done. Disconnecting", this);
391+
client.Disconnect(true);
392+
InternalLogger.Trace("{0}: Disconnected", this);
393+
}
394+
}
395+
377396
/// <summary>
378397
/// Create buffer for body
379398
/// </summary>
@@ -414,6 +433,35 @@ private StringBuilder CreateBodyBuffer(IEnumerable<AsyncLogEventInfo> events, Lo
414433
return bodyBuffer;
415434
}
416435

436+
/// <summary>
437+
/// Handle <paramref name="pickupDirectoryLocation"/> if it is a virtual directory.
438+
/// </summary>
439+
internal string ResolvePickupDirectoryLocationFilePath(string pickupDirectoryLocation)
440+
{
441+
const string virtualPathPrefix = "~/";
442+
443+
if (pickupDirectoryLocation.StartsWith(virtualPathPrefix, StringComparison.Ordinal))
444+
{
445+
// Support for Virtual Paths
446+
var root = AppDomain.CurrentDomain.BaseDirectory;
447+
var directory = pickupDirectoryLocation.Substring(virtualPathPrefix.Length).Replace('/', System.IO.Path.DirectorySeparatorChar);
448+
pickupDirectoryLocation = System.IO.Path.Combine(root, directory);
449+
}
450+
451+
string filename;
452+
string pathAndFilename;
453+
while (true)
454+
{
455+
filename = Guid.NewGuid().ToString() + ".eml";
456+
pathAndFilename = System.IO.Path.Combine(pickupDirectoryLocation, filename);
457+
if (!System.IO.File.Exists(pathAndFilename))
458+
break;
459+
}
460+
461+
InternalLogger.Debug("{0}: Writing mail to file: {1}", this, pathAndFilename);
462+
return pathAndFilename;
463+
}
464+
417465
private void CheckRequiredParameters()
418466
{
419467
var smtpAuthentication = RenderLogEvent(SmtpAuthentication, LogEventInfo.CreateNullEvent());
@@ -479,7 +527,7 @@ private MimeMessage CreateMailMessage(LogEventInfo lastEvent, string body)
479527
newBody = newBody?.Replace(Environment.NewLine, "<br/>");
480528
if (newBody?.IndexOf('\n') >= 0)
481529
{
482-
newBody = newBody?.Replace("\n", "<br/>");
530+
newBody = newBody.Replace("\n", "<br/>");
483531
}
484532
}
485533

src/NLog.MailKit/NLog.MailKit.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ If the mail target was already available on your platform, this package will ove
1111

1212
* Compared to the original MailTarget, the following options aren't implemented:
1313

14-
- PickupDirectory
1514
- NTLM auth
1615

1716
* MailKit gives more control of the sockets, so you get the `secureSocketOption` option for free!
@@ -29,6 +28,7 @@ If the mail target was already available on your platform, this package will ove
2928
<AssemblyOriginatorKeyFile>NLog.snk</AssemblyOriginatorKeyFile>
3029
<DelaySign>false</DelaySign>
3130
<PackageReleaseNotes>
31+
- Added support for PickupDirectoryLocation
3232
- Added support for email headers
3333
- Added target-alias mailkit
3434
- Updated to NLog v5.2.2

test/NLog.MailKit.Tests/IntegrationTests/MailTargetIntegrationTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,34 @@ public void SendMailWithHeaderFooter()
8989
Assert.Contains("*** End ***", mailBody);
9090
}
9191

92+
[Fact]
93+
public void SendMailWitPickupFolder()
94+
{
95+
// Arrange
96+
var tempFolder = Path.Combine(Path.GetTempPath(), "NLog_MailKit_" + Guid.NewGuid().ToString());
97+
try
98+
{
99+
Directory.CreateDirectory(tempFolder);
100+
101+
// Act
102+
var mailTarget = CreateNLogConfig();
103+
mailTarget.PickupDirectoryLocation = tempFolder;
104+
var logger = LogManager.GetLogger("logger1");
105+
var expectedMessage = "hello first mail!";
106+
logger.Info(expectedMessage);
107+
108+
// Assert
109+
var files = Directory.GetFiles(tempFolder);
110+
Assert.Single(files);
111+
var msg = MimeKit.MimeMessage.Load(files[0]);
112+
Assert.Contains(expectedMessage, msg.Body.ToString());
113+
}
114+
finally
115+
{
116+
Directory.Delete(tempFolder, true);
117+
}
118+
}
119+
92120
[Fact]
93121
public void SendMailBatch()
94122
{

0 commit comments

Comments
 (0)