48 lines
2.4 KiB
C#
48 lines
2.4 KiB
C#
using System.Text;
|
|
|
|
namespace WpywMail.Native;
|
|
|
|
public sealed record ParsedMime(string From, string To, string Subject, string MessageId, string Text);
|
|
|
|
public static class Mime
|
|
{
|
|
public static ParsedMime Parse(byte[] raw)
|
|
{
|
|
var value = Encoding.UTF8.GetString(raw).Replace("\r\n", "\n");
|
|
var split = value.IndexOf("\n\n", StringComparison.Ordinal);
|
|
var headerText = split >= 0 ? value[..split] : value;
|
|
var body = split >= 0 ? value[(split + 2)..] : "";
|
|
var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
string? current = null;
|
|
foreach (var line in headerText.Split('\n'))
|
|
{
|
|
if ((line.StartsWith(' ') || line.StartsWith('\t')) && current is not null) headers[current] += " " + line.Trim();
|
|
else { var colon = line.IndexOf(':'); if (colon > 0) { current = line[..colon]; headers[current] = line[(colon + 1)..].Trim(); } }
|
|
}
|
|
return new ParsedMime(headers.GetValueOrDefault("From", ""), headers.GetValueOrDefault("To", ""), headers.GetValueOrDefault("Subject", "(无主题)"), headers.GetValueOrDefault("Message-ID", ""), body.TrimEnd());
|
|
}
|
|
|
|
public static byte[] Build(string from, string[] to, string subject, string text)
|
|
{
|
|
var body = WrapBase64(Encoding.UTF8.GetBytes(text));
|
|
var value = $"From: {from}\r\nTo: {string.Join(", ", to)}\r\nSubject: {EncodeHeader(subject)}\r\nDate: {DateTimeOffset.UtcNow:R}\r\nMessage-ID: <{Guid.NewGuid():N}@wpyw.site>\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Transfer-Encoding: base64\r\n\r\n{body}\r\n";
|
|
return Encoding.UTF8.GetBytes(value);
|
|
}
|
|
|
|
private static string EncodeHeader(string value)
|
|
{
|
|
if (value.All(ch => ch <= 0x7F)) return value;
|
|
var encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
|
|
return $"=?UTF-8?B?{encoded}?=";
|
|
}
|
|
|
|
private static string WrapBase64(byte[] bytes)
|
|
{
|
|
var encoded = Convert.ToBase64String(bytes);
|
|
return string.Join("\r\n", Enumerable.Range(0, (encoded.Length + 75) / 76)
|
|
.Select(index => encoded.Substring(index * 76, Math.Min(76, encoded.Length - index * 76))));
|
|
}
|
|
|
|
public static string[] Addresses(string value) => value.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Select(x => x.Contains('<') ? x[(x.IndexOf('<') + 1)..x.IndexOf('>')] : x).Where(x => x.Contains('@')).ToArray();
|
|
}
|