Lijkt te werken
1e61c8d477300d8b4d16457b2b0808f8309b7956
12 files changed
Eml2MimePart/Helpers.csEml2MimePart/MimePart.csEml2MimePart/QuotedPrintableDecoder.csEml2MimePart/RegexHelper.csEml2Pdf/DataModel.csEml2Pdf/EmailDecoder.csEml2Pdf/Eml2Pdf.csprojEml2Pdf/PdfHelper2.csEml2PdfWinApp/Eml2PdfWinApp.csprojEml2PdfWinApp/Form1.Designer.csEml2PdfWinApp/Form1.csEml2PdfWinApp/pdf/MustExist.txt
diff --git a/Eml2MimePart/Helpers.cs b/Eml2MimePart/Helpers.cs
new file mode 100644
index 0000000..b13bf82
--- /dev/null
+++ b/Eml2MimePart/Helpers.cs
@@ -0,0 +1,86 @@
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Eml2MimePart;
+
+public class Helpers
+{
+ public static string QuotedPrintableDecode(string input, Encoding encoding)
+ {
+ if (string.IsNullOrEmpty(input))
+ return input;
+
+ var byteList = new List<byte>();
+ int i = 0;
+
+ while (i < input.Length)
+ {
+ if (input[i] == '=' && i + 1 < input.Length)
+ {
+ if (input[i + 1] == '\r' || input[i + 1] == '\n')
+ {
+ i++;
+ while (i < input.Length && (input[i] == '\r' || input[i] == '\n'))
+ i++;
+ continue;
+ }
+ if (i + 2 < input.Length && IsHexDigit(input[i + 1]) && IsHexDigit(input[i + 2]))
+ {
+ string hex = input.Substring(i + 1, 2);
+ byte b = Convert.ToByte(hex, 16);
+ byteList.Add(b);
+ i += 3;
+ }
+ else
+ {
+ byteList.AddRange(encoding.GetBytes("="));
+ i++;
+ }
+ }
+ else
+ {
+ byteList.AddRange(encoding.GetBytes(input[i].ToString()));
+ i++;
+ }
+ }
+
+ return encoding.GetString(byteList.ToArray());
+ }
+
+ private static bool IsHexDigit(char c)
+ {
+ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
+ }
+
+ public static string DecodeMimeEncodedWord(string input)
+ {
+ if (string.IsNullOrEmpty(input)) return input;
+
+ var regex = new Regex(@"\=\?([^?]+)\?([QB])\?([^?]+)\?\=", RegexOptions.IgnoreCase);
+ return regex.Replace(input, match =>
+ {
+ string charset = match.Groups[1].Value;
+ string encoding = match.Groups[2].Value.ToUpper();
+ string encodedText = match.Groups[3].Value;
+
+ try
+ {
+ Encoding enc = Encoding.GetEncoding(charset);
+ if (encoding == "Q")
+ {
+ return QuotedPrintableDecode(encodedText, enc);
+ }
+ else if (encoding == "B")
+ {
+ byte[] decodedBytes = Convert.FromBase64String(encodedText);
+ return enc.GetString(decodedBytes);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Failed to decode MIME encoded-word: {ex.Message}");
+ }
+ return encodedText; // Fallback: return as-is
+ });
+ }
+}
diff --git a/Eml2MimePart/MimePart.cs b/Eml2MimePart/MimePart.cs
index cf576ae..0eaad93 100644
--- a/Eml2MimePart/MimePart.cs
+++ b/Eml2MimePart/MimePart.cs
@@ -1,18 +1,18 @@
-using EmlFastDecoder;
+using System.Net.Mail;
using System.Text;
namespace Eml2MimePart;
public class MimePart(string[] lines)
{
private const int InvalidIndex = -1;
-
private readonly string[] _lines = lines;
private int _start = InvalidIndex;
private int _stop = InvalidIndex;
private int _startContent = InvalidIndex;
private IReadOnlyList<(string Name, string Value)>? headers;
- public IReadOnlyList<(string Name, string Value)> Headers => headers ??= BuildHeaders();
+
+ private IReadOnlyList<MimePart>? parts;
private IReadOnlyList<(string Name, string Value)> BuildHeaders()
{
@@ -29,7 +29,8 @@ public class MimePart(string[] lines)
if (s > 0)
{
var name = line[..s];
- var value = new StringBuilder(line[(s + 1)..].Trim());
+ var decodedValue = Helpers.DecodeMimeEncodedWord(line[(s + 1)..].Trim());
+ var value = new StringBuilder(decodedValue);
// NB: Laatste regel wordt genegeerd als deze exact op 'stop' valt, wat bij MIME-headers zeldzaam is.
while (i < _stop - 1 && RegexHelper.IsValue.IsMatch(_lines[i + 1]))
{
@@ -47,60 +48,35 @@ public class MimePart(string[] lines)
throw new InvalidOperationException("Content boundaries not initialized");
}
- // Retourneert de charset uit Content-Type, of Encoding.UTF8.WebName ("utf-8") als deze niet gespecificeerd is.
- public string CharSet => RegexHelper.CharSet.Match(this["Content-Type"])?.Groups[1].Value ?? Encoding.UTF8.WebName;
-
- // Retourneert de tekstuele inhoud van deze MIME-part, gedecodeerd volgens de Content-Transfer-Encoding.
- public string TextContent
+ // Parseert multipart MIME-content en retourneert een lijst van sub-parts gescheiden door de boundary.
+ private IReadOnlyList<MimePart> ParseMultipart(string boundary)
{
- get
- {
- EnsureBoundariesInitialized();
-
- var val = string.Join(Environment.NewLine, _lines[_startContent.._stop]);
-
- return this["Content-Transfer-Encoding"] switch
- {
- "7bit" => val, // Pure ASCII, geen decoding nodig
- "8bit" => val, // 8-bit tekst, vertrouw op CharSet
- "quoted-printable" => QuotedPrintableDecoder.Decode(val, CharSet),
- "base64" => throw new InvalidOperationException("Base64 content cannot be represented as text"),
- "binary" => throw new InvalidOperationException("Binary content cannot be represented as text"),
- "" => val, // Geen encoding gespecificeerd, assumeer plain text
- var enc => throw new NotSupportedException($"Unsupported Content-Transfer-Encoding: {enc}")
- };
- }
- }
+ var result = new List<MimePart>();
+ MimePart? currentPart = null;
- // Retourneert de binaire inhoud van deze MIME-part, gedecodeerd volgens de Content-Transfer-Encoding.
- public byte[] BinaryContent
- {
- get
+ for (int i = _start; i < _stop; i++)
{
- EnsureBoundariesInitialized();
+ var line = _lines[i];
+ if (line.StartsWith($"{boundary}--"))
+ {
+ if (currentPart != null) // Optioneel, maar kan weg
+ currentPart._stop = i - 1;
+ break;
+ }
- var val = string.Join("", _lines[_startContent.._stop]);
- return this["Content-Transfer-Encoding"] switch
+ if (line.StartsWith(boundary))
{
- "7bit" => Encoding.ASCII.GetBytes(val), // Alleen ASCII, veilig als bytes
- "8bit" => Encoding.GetEncoding(CharSet).GetBytes(val), // Gebruik de gespecificeerde charset
- "binary" => Encoding.GetEncoding(CharSet).GetBytes(val), // NB: Binary data kan corrupt raken; overweeg byte[] input voor echte support
- "quoted-printable" => Encoding.GetEncoding(CharSet).GetBytes(QuotedPrintableDecoder.Decode(val, CharSet)),
- "base64" => Convert.FromBase64String(val),
- "" => Encoding.UTF8.GetBytes(val), // Geen encoding, default UTF-8
- var enc => throw new NotSupportedException($"Unsupported Content-Transfer-Encoding: {enc}")
- };
+ if (currentPart is not null)
+ currentPart._stop = i - 1;
+ currentPart = new MimePart(_lines) { _start = i + 1 };
+ result.Add(currentPart);
+ }
}
+ if (currentPart is not null && currentPart._stop == InvalidIndex)
+ currentPart._stop = _stop - 1;
+ return result.AsReadOnly();
}
- // Geeft de waarde van een header met de opgegeven naam, of een lege string als deze niet bestaat.
- public string this[string name] => Headers.FirstOrDefault(x => x.Name == name).Value ?? string.Empty;
-
- private IReadOnlyList<MimePart>? parts;
-
- // Retourneert een read-only lijst van sub-parts (attachments of multipart secties).
- public IReadOnlyList<MimePart> Parts => parts ??= BuildParts();
-
private IReadOnlyList<MimePart> BuildParts()
{
var contentType = this["Content-Type"];
@@ -133,43 +109,117 @@ public class MimePart(string[] lines)
return ParseMultipart(boundary);
}
- // Parseert multipart MIME-content en retourneert een lijst van sub-parts gescheiden door de boundary.
- private IReadOnlyList<MimePart> ParseMultipart(string boundary)
+ // Only valif for attachements
+ public string FileName
{
- var result = new List<MimePart>();
- MimePart? currentPart = null;
+ get
+ {
+ var contentType = this["Content-Type"];
- for (int i = _start; i < _stop; i++)
+ var match = RegexHelper.FileName.Match(contentType);
+
+ if (!match.Success)
+ return string.Empty;
+
+ return match.Groups[1].Value;
+ }
+ }
+
+
+ public string ContentId
+ {
+ get
{
- var line = _lines[i];
- if (line.StartsWith($"{boundary}--"))
+ var contentId = this["Content-ID"];
+
+ var match = RegexHelper.ContentId.Match(contentId);
+
+ if (!match.Success)
+ return string.Empty;
+
+ return match.Groups[1].Value;
+ }
+ }
+
+
+ // Headers van een part indien aanwezig
+ public IReadOnlyList<(string Name, string Value)> Headers => headers ??= BuildHeaders();
+
+ // Retourneert de charset uit Content-Type, of Encoding.UTF8.WebName ("utf-8") als deze niet gespecificeerd is.
+ //public string CharSet => RegexHelper.CharSet.Match(this["Content-Type"])?.Groups[1].Value ?? Encoding.UTF8.WebName;
+
+ public string CharSet
+ {
+ get
+ {
+ string contentType = this["Content-Type"];
+ if (string.IsNullOrEmpty(contentType))
+ return Encoding.UTF8.WebName;
+
+ var match = RegexHelper.CharSet.Match(contentType);
+ return match?.Success == true ? match.Groups[1].Value : Encoding.UTF8.WebName;
+ }
+ }
+
+ // Retourneert de tekstuele inhoud van deze MIME-part, gedecodeerd volgens de Content-Transfer-Encoding.
+ public string TextContent
+ {
+ get
+ {
+ EnsureBoundariesInitialized();
+
+ var val = string.Join(Environment.NewLine, _lines[_startContent.._stop]);
+
+ return this["Content-Transfer-Encoding"] switch
{
- if (currentPart != null) // Optioneel, maar kan weg
- currentPart._stop = i - 1;
- break;
- }
+ "7bit" => val, // Pure ASCII, geen decoding nodig
+ "8bit" => val, // 8-bit tekst, vertrouw op CharSet
+ "quoted-printable" => Helpers.QuotedPrintableDecode(val, Encoding.GetEncoding( CharSet )),
+ "base64" => Encoding.GetEncoding(CharSet).GetString( Convert.FromBase64String(string.Join(string.Empty, _lines[_startContent.._stop]))),
+ "binary" => throw new InvalidOperationException("Binary content cannot be represented as text"),
+ "" => val, // Geen encoding gespecificeerd, assumeer plain text
+ var enc => throw new NotSupportedException($"Unsupported Content-Transfer-Encoding: {enc}")
+ };
+ }
+ }
- if (line.StartsWith(boundary))
+ // Retourneert de binaire inhoud van deze MIME-part, gedecodeerd volgens de Content-Transfer-Encoding.
+ public byte[] BinaryContent
+ {
+ get
+ {
+ EnsureBoundariesInitialized();
+
+ var val = string.Join("", _lines[_startContent.._stop]);
+ var enc = Encoding.GetEncoding(CharSet);
+ return this["Content-Transfer-Encoding"] switch
{
- if (currentPart is not null)
- currentPart._stop = i - 1;
- currentPart = new MimePart(_lines) { _start = i + 1 };
- result.Add(currentPart);
- }
+ "7bit" => Encoding.ASCII.GetBytes(val), // Alleen ASCII, veilig als bytes
+ "8bit" => Encoding.GetEncoding(CharSet).GetBytes(val), // Gebruik de gespecificeerde charset
+ "binary" => Encoding.GetEncoding(CharSet).GetBytes(val), // NB: Binary data kan corrupt raken; overweeg byte[] input voor echte support
+ "quoted-printable" => enc.GetBytes(Helpers.QuotedPrintableDecode(val, enc)),
+ "base64" => Convert.FromBase64String(val),
+ "" => Encoding.UTF8.GetBytes(val), // Geen encoding, default UTF-8
+ var enco => throw new NotSupportedException($"Unsupported Content-Transfer-Encoding: {enco}")
+ };
}
- if (currentPart is not null && currentPart._stop == InvalidIndex)
- currentPart._stop = _stop - 1;
- return result.AsReadOnly();
}
+ // Geeft de waarde van een header met de opgegeven naam, of een lege string als deze niet bestaat.
+ public string this[string name] => Headers.FirstOrDefault(x => x.Name == name).Value ?? string.Empty;
+
+ // Retourneert een read-only lijst van sub-parts (attachments of multipart secties).
+ public IReadOnlyList<MimePart> Parts => parts ??= BuildParts();
+
// Slaat deze MIME-part asynchroon op naar een bestand met de huidige datum/tijd.
- public Task SaveAsync(string path) => SaveAsync(path, DateTime.Now);
+ public Task SaveAsync(string path, CancellationToken ct = default) => SaveAsync(path, DateTime.Now, ct);
- // Slaat deze MIME-part asynchroon op naar een bestand met de opgegeven datum/tijd.
- public async Task SaveAsync(string path, DateTime dtm)
+ // Slaat deze MIME-part asynchroon op (met encoding) naar een bestand met de opgegeven datum/tijd.
+ public async Task SaveAsync(string path, DateTime dtm, CancellationToken ct = default)
{
EnsureBoundariesInitialized();
- await File.WriteAllLinesAsync(path, _lines[_startContent.._stop]);
+ var encoding = string.IsNullOrWhiteSpace(CharSet) ? Encoding.UTF8 : Encoding.GetEncoding(CharSet);
+ await File.WriteAllLinesAsync(path, _lines[_startContent.._stop], encoding, ct);
File.SetAttributes(path, FileAttributes.ReadOnly);
File.SetLastWriteTimeUtc(path, dtm);
File.SetCreationTime(path, dtm);
diff --git a/Eml2MimePart/QuotedPrintableDecoder.cs b/Eml2MimePart/QuotedPrintableDecoder.cs
deleted file mode 100644
index 3019cc7..0000000
--- a/Eml2MimePart/QuotedPrintableDecoder.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System.Text;
-
-namespace Eml2MimePart;
-
-public static class QuotedPrintableDecoder
-{
- public static string Decode(string input, string charSet = "UTF-8")
- {
- if (string.IsNullOrEmpty(input))
- return input;
-
- Encoding encoding = Encoding.GetEncoding(charSet);
- var byteList = new List<byte>();
- int i = 0;
-
- while (i < input.Length)
- {
- if (input[i] == '=' && i + 1 < input.Length)
- {
- if (input[i + 1] == '\r' || input[i + 1] == '\n')
- {
- i++;
- while (i < input.Length && (input[i] == '\r' || input[i] == '\n'))
- i++;
- continue;
- }
- if (i + 2 < input.Length && IsHexDigit(input[i + 1]) && IsHexDigit(input[i + 2]))
- {
- string hex = input.Substring(i + 1, 2);
- byte b = Convert.ToByte(hex, 16);
- byteList.Add(b);
- i += 3;
- }
- else
- {
- byteList.AddRange(encoding.GetBytes("="));
- i++;
- }
- }
- else
- {
- byteList.AddRange(encoding.GetBytes(input[i].ToString()));
- i++;
- }
- }
-
- return encoding.GetString(byteList.ToArray());
- }
-
- private static bool IsHexDigit(char c)
- {
- return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
- }
-}
diff --git a/Eml2MimePart/RegexHelper.cs b/Eml2MimePart/RegexHelper.cs
index 3bf40ec..829e226 100644
--- a/Eml2MimePart/RegexHelper.cs
+++ b/Eml2MimePart/RegexHelper.cs
@@ -7,6 +7,8 @@ public partial class RegexHelper
public static readonly Regex IsValue = IsValueRegex();
public static readonly Regex Boundary = BoundaryRegex();
public static readonly Regex CharSet = CharsetRegex();
+ public static readonly Regex FileName = FileNameRegex();
+ public static readonly Regex ContentId = ContentIdRegex();
[GeneratedRegex(@"^\W")]
private static partial Regex IsValueRegex();
@@ -16,4 +18,10 @@ public partial class RegexHelper
[GeneratedRegex(@"\Wcharset=""([^""]*)""")]
private static partial Regex CharsetRegex();
+
+ [GeneratedRegex(@"\Wname=""([^""]+)""")]
+ private static partial Regex FileNameRegex();
+
+ [GeneratedRegex(@"<([^>]+)>")]
+ private static partial Regex ContentIdRegex();
}
diff --git a/Eml2Pdf/DataModel.cs b/Eml2Pdf/DataModel.cs
index 4b0b07f..1a22797 100644
--- a/Eml2Pdf/DataModel.cs
+++ b/Eml2Pdf/DataModel.cs
@@ -2,19 +2,19 @@
public class EmailMessage
{
- public List<(string Name, string Value)> Headers { get; set; } = new List<(string, string)>();
- public List<MimePart> Parts { get; set; } = new List<MimePart>();
- public List<Attachment> Attachments { get; set; } = new List<Attachment>();
+ public List<(string Name, string Value)> Headers { get; set; } = [];
+ public List<MimePart2> Parts { get; set; } = [];
+ public List<Attachment> Attachments { get; set; } = [];
// Optionele convenience properties voor veelgebruikte headers
- public string From => Headers.FirstOrDefault(h => h.Name.ToLower() == "from").Value;
- public string To => Headers.FirstOrDefault(h => h.Name.ToLower() == "to").Value;
- public string Subject => Headers.FirstOrDefault(h => h.Name.ToLower() == "subject").Value;
- public DateTime? Date => DateTime.TryParse(Headers.FirstOrDefault(h => h.Name.ToLower() == "date").Value, out DateTime date) ? date : (DateTime?)null;
- public string MessageId => Headers.FirstOrDefault(h => h.Name.ToLower() == "message-id").Value;
+ public string From => Headers.FirstOrDefault(h => h.Name.Equals("from", StringComparison.CurrentCultureIgnoreCase)).Value;
+ public string To => Headers.FirstOrDefault(h => h.Name.Equals("to", StringComparison.CurrentCultureIgnoreCase)).Value;
+ public string Subject => Headers.FirstOrDefault(h => h.Name.Equals("subject", StringComparison.CurrentCultureIgnoreCase)).Value;
+ public DateTime? Date => DateTime.TryParse(Headers.FirstOrDefault(h => h.Name.Equals("date", StringComparison.CurrentCultureIgnoreCase)).Value, out DateTime date) ? date : (DateTime?)null;
+ public string MessageId => Headers.FirstOrDefault(h => h.Name.Equals("message-id", StringComparison.CurrentCultureIgnoreCase)).Value;
}
-public class MimePart
+public class MimePart2
{
public string ContentType { get; set; }
public string Charset { get; set; }
diff --git a/Eml2Pdf/EmailDecoder.cs b/Eml2Pdf/EmailDecoder.cs
index a6c542b..eb83b47 100644
--- a/Eml2Pdf/EmailDecoder.cs
+++ b/Eml2Pdf/EmailDecoder.cs
@@ -185,7 +185,7 @@ public class EmailDecoder
private static void ProcessSinglePart(EmailMessage email, string part, string contentType, Dictionary<string, string> parameters, string parentBoundary = null)
{
- var mimePart = new MimePart
+ var mimePart = new MimePart2
{
ContentType = contentType,
Charset = parameters.ContainsKey("charset") ? parameters["charset"] : "utf-8",
diff --git a/Eml2Pdf/Eml2Pdf.csproj b/Eml2Pdf/Eml2Pdf.csproj
index 5eb16c2..4259fac 100644
--- a/Eml2Pdf/Eml2Pdf.csproj
+++ b/Eml2Pdf/Eml2Pdf.csproj
@@ -13,4 +13,8 @@
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
</ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Eml2MimePart\Eml2MimePart.csproj" />
+ </ItemGroup>
+
</Project>
diff --git a/Eml2Pdf/PdfHelper2.cs b/Eml2Pdf/PdfHelper2.cs
new file mode 100644
index 0000000..938e392
--- /dev/null
+++ b/Eml2Pdf/PdfHelper2.cs
@@ -0,0 +1,106 @@
+using Eml2MimePart;
+using PdfSharp.Pdf;
+using System.Text;
+using TheArtOfDev.HtmlRenderer.PdfSharp;
+
+namespace Eml2Pdf2;
+
+public class PdfHelper2
+{
+ private static string HtmlEscape(string text) => text.Replace("<", "<").Replace(">", ">");
+
+ private static string GetHtml(MimePart email)
+ {
+ if (email["Content-Type"].Contains("text/html"))
+ return email.TextContent;
+ foreach(var part in email.Parts)
+ {
+ var html = GetHtml(part);
+ if (!string.IsNullOrEmpty(html))
+ return html;
+ }
+ return string.Empty;
+ }
+
+ public static void GetAttachements(List<MimePart> attachements, MimePart part)
+ {
+ var contentType = part["Content-Type"];
+ if (!string.IsNullOrWhiteSpace(contentType) && !contentType.StartsWith("text/") && !contentType.StartsWith("multipart/"))
+ attachements.Add(part);
+
+ foreach (var subpart in part.Parts)
+ {
+ GetAttachements(attachements, subpart);
+ }
+ }
+
+ public static void CreatePdf(MimePart email, string pdfPath)
+ {
+ var html = GetHtml(email);
+
+ if (string.IsNullOrWhiteSpace(html))
+ return;
+
+ int index = html.IndexOf("<body");
+ if (index != -1)
+ {
+ int eindIndex = html.IndexOf('>', index);
+ if (eindIndex != -1)
+ {
+ var htmlTable = new StringBuilder(Environment.NewLine);
+ htmlTable.AppendLine("<table border='1' style='width: 100%; border-collapse: collapse; margin-bottom: 20px;'>");
+ string[] importantHeaders = ["From", "Subject", "To", "Date"];
+ foreach (var header in importantHeaders)
+ {
+ htmlTable.AppendLine("<tr>");
+ htmlTable.AppendLine($"<td style='padding: 5px; width:60px'>{header}</td>");
+ htmlTable.AppendLine($"<td style='padding: 5px;'>{HtmlEscape(email[header])}</td>");
+ htmlTable.AppendLine("</tr>");
+ }
+ htmlTable.Append("</table>");
+ html = html.Insert(eindIndex + 1, htmlTable.ToString());
+ }
+ }
+
+ var dir = Path.Combine(AppContext.BaseDirectory, "tmp");
+
+ List<MimePart> attachements = [];
+ GetAttachements(attachements, email);
+
+ if (attachements.Count > 0)
+ {
+ Directory.CreateDirectory(dir);
+
+ // save attachements temporary
+ foreach (var attachment in attachements)
+ {
+ var path = Path.Combine(dir, attachment.FileName);
+
+ File.WriteAllBytes(path, attachment.BinaryContent);
+
+ html = html.Replace($"cid:{attachment.ContentId}", $"file:///{path.Replace('\\', '/')}");
+ }
+ }
+
+ // DEBUGGING
+ File.WriteAllText("test.html", html);
+
+ PdfDocument pdf = PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.A4);
+
+ pdf.Save(pdfPath);
+
+ if (attachements.Count > 0)
+ {
+ // clear attachements
+ foreach (var attachment in attachements)
+ {
+ var path = Path.Combine(dir, attachment.FileName);
+ if (File.Exists(path))
+ File.Delete(path);
+ }
+ }
+
+ }
+
+
+}
diff --git a/Eml2PdfWinApp/Eml2PdfWinApp.csproj b/Eml2PdfWinApp/Eml2PdfWinApp.csproj
index c03511e..fac6fbe 100644
--- a/Eml2PdfWinApp/Eml2PdfWinApp.csproj
+++ b/Eml2PdfWinApp/Eml2PdfWinApp.csproj
@@ -14,15 +14,15 @@
</ItemGroup>
<ItemGroup>
- <None Update="input\**">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Update="output\MustExist.txt">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Update="input\MustExist.txt">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
+ <None Update="input\**">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Update="output\**">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Update="pdf\**">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
</ItemGroup>
</Project>
\ No newline at end of file
diff --git a/Eml2PdfWinApp/Form1.Designer.cs b/Eml2PdfWinApp/Form1.Designer.cs
index df813ed..5059b13 100644
--- a/Eml2PdfWinApp/Form1.Designer.cs
+++ b/Eml2PdfWinApp/Form1.Designer.cs
@@ -32,6 +32,7 @@
textBox2 = new TextBox();
button1 = new Button();
button2 = new Button();
+ textBox3 = new TextBox();
SuspendLayout();
//
// textBox1
@@ -54,9 +55,9 @@
//
// button1
//
- button1.Location = new Point(336, 37);
+ button1.Location = new Point(320, 98);
button1.Name = "button1";
- button1.Size = new Size(75, 23);
+ button1.Size = new Size(125, 23);
button1.TabIndex = 2;
button1.Text = "Eml2Pdf";
button1.UseVisualStyleBackColor = true;
@@ -64,7 +65,7 @@
//
// button2
//
- button2.Location = new Point(451, 37);
+ button2.Location = new Point(320, 57);
button2.Name = "button2";
button2.Size = new Size(125, 23);
button2.TabIndex = 3;
@@ -72,11 +73,21 @@
button2.UseVisualStyleBackColor = true;
button2.Click += Button2_Click;
//
+ // textBox3
+ //
+ textBox3.Location = new Point(35, 120);
+ textBox3.Name = "textBox3";
+ textBox3.PlaceholderText = "pdf directory";
+ textBox3.Size = new Size(269, 23);
+ textBox3.TabIndex = 4;
+ textBox3.Text = "pdf";
+ //
// Form1
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
- ClientSize = new Size(896, 144);
+ ClientSize = new Size(468, 164);
+ Controls.Add(textBox3);
Controls.Add(button2);
Controls.Add(button1);
Controls.Add(textBox2);
@@ -93,5 +104,6 @@
private TextBox textBox2;
private Button button1;
private Button button2;
+ private TextBox textBox3;
}
}
diff --git a/Eml2PdfWinApp/Form1.cs b/Eml2PdfWinApp/Form1.cs
index 8dd7e1d..f5f5267 100644
--- a/Eml2PdfWinApp/Form1.cs
+++ b/Eml2PdfWinApp/Form1.cs
@@ -1,4 +1,5 @@
-using Eml2Pdf;
+using Eml2MimePart;
+using System.Diagnostics;
using System.Text;
namespace Eml2PdfWinApp
@@ -18,15 +19,16 @@ namespace Eml2PdfWinApp
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
- foreach (var emlPath in Directory.GetFiles(this.textBox1.Text, "*.eml"))
+ foreach (var emlPath in Directory.GetFiles(this.textBox2.Text, "*.eml"))
{
var name = Path.GetFileNameWithoutExtension(emlPath);
- var pdfPath = Path.Combine(this.textBox2.Text, $"{name}.pdf");
+ var pdfPath = Path.Combine(this.textBox3.Text, $"{name}.pdf");
+
+ var email = await MimePart.ReadEmlAsync(emlPath);
- var email = await EmailDecoder.ParseEmlAsync(emlPath);
+ Eml2Pdf2.PdfHelper2.CreatePdf(email, pdfPath);
- PdfHelper.CreatePdf(email, pdfPath);
}
this.textBox1.Enabled = true;
@@ -34,6 +36,34 @@ namespace Eml2PdfWinApp
this.button1.Enabled = true;
}
+ static async Task SaveMailAttachementsAsync(string OutputDir, MimePart part)
+ {
+ var contentType = part["Content-Type"];
+
+ if (part["Content-Disposition"].Contains("attachment"))
+ {
+ if (contentType.Contains("message/rfc822"))
+ {
+ if (DateTime.TryParse(part.Parts[0]["Date"], out DateTime dtm))
+ {
+ var outputPath = Path.Combine(OutputDir, $"{dtm:yyyyMMdd-HHmmss}.eml");
+
+ await part.SaveAsync(outputPath, dtm);
+ }
+ else
+ {
+ Debug.WriteLine("No Date found in message/rfc822 attachement");
+ }
+ }
+ }
+
+ foreach (var subpart in part.Parts)
+ {
+ await SaveMailAttachementsAsync(OutputDir, subpart);
+ }
+ }
+
+
private async void Button2_Click(object sender, EventArgs e)
{
this.button2.Enabled = false;
@@ -42,15 +72,9 @@ namespace Eml2PdfWinApp
foreach (var emlPath in Directory.GetFiles(this.textBox1.Text, "*.eml"))
{
- var name = Path.GetFileNameWithoutExtension(emlPath);
-
- var email = await EmailDecoder.ParseEmlAsync(emlPath);
-
- foreach(var emailA in email.Parts)
- {
-
- }
+ var email = await MimePart.ReadEmlAsync(emlPath);
+ await SaveMailAttachementsAsync(this.textBox2.Text, email);
}
this.button2.Enabled = true;
diff --git a/Eml2PdfWinApp/pdf/MustExist.txt b/Eml2PdfWinApp/pdf/MustExist.txt
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/Eml2PdfWinApp/pdf/MustExist.txt
@@ -0,0 +1 @@
+
\ No newline at end of file