diff --git a/Documentation/Examples/SmtpExamples.cs b/Documentation/Examples/SmtpExamples.cs
index 2f9d275fea..af99028989 100644
--- a/Documentation/Examples/SmtpExamples.cs
+++ b/Documentation/Examples/SmtpExamples.cs
@@ -62,7 +62,7 @@ public static void SaveToPickupDirectory (MimeMessage message, string pickupDire
// which means that lines beginning with "." need to be escaped
// by adding an extra "." to the beginning of the line.
//
- // Use an SmtpDataFilter "byte-stuff" the message as it is written
+ // Use an SmtpDataFilter to "byte-stuff" the message as it is written
// to the file stream. This is the same process that an SmtpClient
// would use when sending the message in a `DATA` command.
using (var filtered = new FilteredStream (stream)) {
@@ -89,6 +89,26 @@ public static void SaveToPickupDirectory (MimeMessage message, string pickupDire
}
#endregion
+ #region LoadFromPickupDirectory
+ public static MimeMessage LoadFromPickupDirectory (string fileName)
+ {
+ using (var stream = File.OpenRead (fileName)) {
+ // IIS pickup directories store messages that have been "byte-stuffed"
+ // which means that lines beginning with "." have been escaped by
+ // adding an extra "." to the beginning of the line.
+ //
+ // Use an SmtpDataFilter to decode the message as it is loaded from
+ // the file stream. This is the reverse process that an SmtpClient
+ // would use when sending the message in a `DATA` command.
+ using (var filtered = new FilteredStream (stream)) {
+ filtered.Add (new SmtpDataFilter (decode: true));
+
+ return MimeMessage.Load (filtered);
+ }
+ }
+ }
+ #endregion
+
#region ProtocolLogger
public static void SendMessage (MimeMessage message)
{
diff --git a/MailKit/Net/Smtp/SmtpDataFilter.cs b/MailKit/Net/Smtp/SmtpDataFilter.cs
index 8a6759d152..898c4cbbcf 100644
--- a/MailKit/Net/Smtp/SmtpDataFilter.cs
+++ b/MailKit/Net/Smtp/SmtpDataFilter.cs
@@ -31,15 +31,17 @@ namespace MailKit.Net.Smtp {
/// An SMTP filter designed to format a message stream for the DATA command.
///
///
- /// A special stream filter that escapes lines beginning with a '.' as needed when
- /// sending a message via the SMTP protocol or when saving a message to an IIS
- /// message pickup directory.
+ /// A special stream filter that can encode or decode lines beginning with a '.' as
+ /// needed when sending/receiving a message via the SMTP protocol or when saving a
+ /// message to an IIS message pickup directory.
///
///
///
+ ///
///
public class SmtpDataFilter : MimeFilterBase
{
+ readonly bool decode;
bool bol;
///
@@ -48,29 +50,18 @@ public class SmtpDataFilter : MimeFilterBase
///
/// Creates a new .
///
+ /// true if the filter should decode the content; otherwise, false.
///
///
+ ///
///
- public SmtpDataFilter ()
+ public SmtpDataFilter (bool decode = false)
{
+ this.decode = decode;
bol = true;
}
- ///
- /// Filter the specified input.
- ///
- ///
- /// Filters the specified input buffer starting at the given index,
- /// spanning across the specified number of bytes.
- ///
- /// The filtered output.
- /// The input buffer.
- /// The starting index of the input buffer.
- /// The length of the input buffer, starting at .
- /// The output index.
- /// The output length.
- /// If set to true, all internally buffered data should be flushed to the output buffer.
- protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush)
+ byte[] Encode (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush)
{
int inputEnd = startIndex + length;
bool escape = bol;
@@ -125,6 +116,51 @@ protected override byte[] Filter (byte[] input, int startIndex, int length, out
return OutputBuffer;
}
+ byte[] Decode (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush)
+ {
+ int inputEnd = startIndex + length;
+ int index = startIndex;
+
+ EnsureOutputSize (length, false);
+ outputLength = 0;
+ outputIndex = 0;
+
+ while (index < inputEnd) {
+ byte c = input[index++];
+
+ if (bol && c == (byte) '.') {
+ bol = false;
+ } else {
+ OutputBuffer[outputLength++] = c;
+ bol = c == (byte) '\n';
+ }
+ }
+
+ return OutputBuffer;
+ }
+
+ ///
+ /// Filter the specified input.
+ ///
+ ///
+ /// Filters the specified input buffer starting at the given index,
+ /// spanning across the specified number of bytes.
+ ///
+ /// The filtered output.
+ /// The input buffer.
+ /// The starting index of the input buffer.
+ /// The length of the input buffer, starting at .
+ /// The output index.
+ /// The output length.
+ /// If set to true, all internally buffered data should be flushed to the output buffer.
+ protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush)
+ {
+ if (decode)
+ return Decode (input, startIndex, length, out outputIndex, out outputLength, flush);
+
+ return Encode (input, startIndex, length, out outputIndex, out outputLength, flush);
+ }
+
///
/// Reset the filter.
///
diff --git a/UnitTests/Net/Smtp/SmtpDataFilterTests.cs b/UnitTests/Net/Smtp/SmtpDataFilterTests.cs
index 69f625c383..bc5c11be83 100644
--- a/UnitTests/Net/Smtp/SmtpDataFilterTests.cs
+++ b/UnitTests/Net/Smtp/SmtpDataFilterTests.cs
@@ -41,6 +41,38 @@ public class SmtpDataFilterTests
const string ComplexDataInput = "This is a bit more complicated\r\n... This line starts with a '.' and\r\ntherefore needs to be byte-stuffed\r\n. And so does this line!\r\n";
const string ComplexDataOutput = "This is a bit more complicated\r\n.... This line starts with a '.' and\r\ntherefore needs to be byte-stuffed\r\n.. And so does this line!\r\n";
+ [Test]
+ public void TestSmtpDataFilterDecode ()
+ {
+ var inputs = new string[] { SimpleDataInput, ComplexDataOutput };
+ var outputs = new string[] { SimpleDataInput, ComplexDataInput };
+ var filter = new SmtpDataFilter (decode: true);
+
+ for (int i = 0; i < inputs.Length; i++) {
+ using (var memory = new MemoryStream ()) {
+ byte[] buffer;
+ int n;
+
+ using (var filtered = new FilteredStream (memory)) {
+ filtered.Add (filter);
+
+ buffer = Encoding.ASCII.GetBytes (inputs[i]);
+ filtered.Write (buffer, 0, buffer.Length);
+ filtered.Flush ();
+ }
+
+ buffer = memory.GetBuffer ();
+ n = (int) memory.Length;
+
+ var text = Encoding.ASCII.GetString (buffer, 0, n);
+
+ Assert.AreEqual (outputs[i], text);
+
+ filter.Reset ();
+ }
+ }
+ }
+
[Test]
public void TestSmtpDataFilter ()
{