Files
Wino-Mail/Wino.Core/Extensions/MimeExtensions.cs

110 lines
3.5 KiB
C#
Raw Normal View History

using System;
using System.IO;
2024-04-18 01:44:37 +02:00
using System.Text;
using Google.Apis.Gmail.v1.Data;
using HtmlAgilityPack;
2024-04-18 01:44:37 +02:00
using MimeKit;
using MimeKit.IO;
using MimeKit.IO.Filters;
using MimeKit.Utils;
using Wino.Services.Extensions;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
namespace Wino.Core.Extensions;
public static class MimeExtensions
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
/// <summary>
/// Returns MimeKit.MimeMessage instance for this GMail Message's Raw content.
/// </summary>
/// <param name="message">GMail message.</param>
public static MimeMessage GetGmailMimeMessage(this Message message)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
if (message == null || message.Raw == null)
return null;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Gmail raw is not base64 but base64Safe. We need to remove this HTML things.
var base64Encoded = message.Raw.Replace(",", "=").Replace("-", "+").Replace("_", "/");
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
byte[] bytes = Encoding.ASCII.GetBytes(base64Encoded);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var stream = new MemoryStream(bytes);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// This method will dispose outer stream.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
using (stream)
{
using var filtered = new FilteredStream(stream);
filtered.Add(DecoderFilter.Create(ContentEncoding.Base64));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
return MimeMessage.Load(filtered);
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Sets html body replacing base64 images with cid linked resources.
/// Updates text body based on html.
/// </summary>
/// <param name="bodyBuilder">Body builder.</param>
/// <param name="htmlContent">Html content that can have embedded images.</param>
/// <returns>Body builder with set HtmlBody.</returns>
public static BodyBuilder SetHtmlBody(this BodyBuilder bodyBuilder, string htmlContent)
{
if (string.IsNullOrEmpty(htmlContent)) return bodyBuilder;
2025-02-16 11:54:23 +01:00
var doc = new HtmlDocument();
doc.LoadHtml(htmlContent);
2025-02-16 11:54:23 +01:00
var imgNodes = doc.DocumentNode.SelectNodes("//img");
2025-02-16 11:54:23 +01:00
if (imgNodes != null)
{
foreach (var node in imgNodes)
{
2025-02-16 11:54:23 +01:00
var src = node.GetAttributeValue("src", string.Empty);
2025-02-16 11:54:23 +01:00
if (string.IsNullOrEmpty(src)) continue;
2025-02-16 11:54:23 +01:00
if (!src.StartsWith("data:image"))
{
continue;
}
2025-02-16 11:54:23 +01:00
var parts = src.Substring(11).Split([";base64,"], StringSplitOptions.None);
2025-02-16 11:54:23 +01:00
string mimeType = parts[0];
string base64Content = parts[1];
2025-02-16 11:54:23 +01:00
var alt = node.GetAttributeValue("alt", $"Embedded_Image.{mimeType}");
2025-02-16 11:54:23 +01:00
// Convert the base64 content to binary data
byte[] imageData = Convert.FromBase64String(base64Content);
2025-02-16 11:54:23 +01:00
// Create a new linked resource as MimePart
var image = new MimePart("image", mimeType)
{
ContentId = MimeUtils.GenerateMessageId(),
Content = new MimeContent(new MemoryStream(imageData)),
ContentDisposition = new ContentDisposition(ContentDisposition.Inline),
ContentDescription = alt.Replace(" ", "_"),
FileName = alt,
ContentTransferEncoding = ContentEncoding.Base64
};
2025-02-16 11:54:23 +01:00
bodyBuilder.LinkedResources.Add(image);
2025-02-16 11:54:23 +01:00
node.SetAttributeValue("src", $"cid:{image.ContentId}");
}
2025-02-16 11:54:23 +01:00
}
2025-02-16 11:54:23 +01:00
bodyBuilder.HtmlBody = doc.DocumentNode.InnerHtml;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
if (!string.IsNullOrEmpty(bodyBuilder.HtmlBody))
{
bodyBuilder.TextBody = HtmlAgilityPackExtensions.GetPreviewText(bodyBuilder.HtmlBody);
}
2025-02-16 11:54:23 +01:00
return bodyBuilder;
2024-04-18 01:44:37 +02:00
}
}