using AIStudio.Tools.Mail;
using MailKit;
using MimeKit;
namespace AIStudio.Tests.Tools.Mail;
///
/// Checks that the structure the server describes tells the same as the parsed mail.
///
///
/// The sync decides from the BODYSTRUCTURE whether to fetch a mail at all, while the fixtures of
/// MailTextBuilderTests check the parsed mails. Both have to agree, or a mail would be fetched,
/// indexed and only then found to be encrypted, or the other way round.
///
[TestFixture]
public sealed class MailEncryptionDetectionTests
{
///
/// Where a part sits in the mail, e.g. "1.2". The detection never looks at it.
///
private const string PART_SPECIFIER = "1";
private static readonly BodyPartBasic PLAIN_TEXT = Part("text", "plain");
[Test]
public void TheStructureFromTheServerTellsTheEncryption()
{
Assert.Multiple(() =>
{
Assert.That(MailEncryptionDetection.Detect(Part("application", "pkcs7-mime", smimeType: "enveloped-data")), Is.EqualTo(MailEncryptionKind.SMIME));
Assert.That(MailEncryptionDetection.Detect(Part("application", "x-pkcs7-mime")), Is.EqualTo(MailEncryptionKind.SMIME), "An S/MIME part without smime-type is readable.");
Assert.That(MailEncryptionDetection.Detect(Part("application", "pkcs7-mime", smimeType: "signed-data")), Is.EqualTo(MailEncryptionKind.SMIME_OPAQUE_SIGNED));
Assert.That(MailEncryptionDetection.Detect(Part("application", "octet-stream", name: "smime.p7m")), Is.EqualTo(MailEncryptionKind.SMIME), "An envelope sent as plain bytes is readable.");
Assert.That(MailEncryptionDetection.Detect(Multipart("encrypted", Part("application", "pgp-encrypted"), Part("application", "octet-stream"))), Is.EqualTo(MailEncryptionKind.PGP_MIME));
Assert.That(MailEncryptionDetection.Detect(Multipart("mixed", PLAIN_TEXT, Part("application", "x-microsoft-rpmsg-message"))), Is.EqualTo(MailEncryptionKind.MICROSOFT_IRM));
Assert.That(MailEncryptionDetection.Detect(Multipart("mixed", PLAIN_TEXT, Part("application", "octet-stream", name: "message.rpmsg"))), Is.EqualTo(MailEncryptionKind.MICROSOFT_IRM), "The IRM attachment is only known by its type.");
});
}
[Test]
public void TheStructureFromTheServerTellsAReadableMail()
{
var attachedEncryptedMail = new BodyPartMessage(new ContentType("message", "rfc822"), PART_SPECIFIER)
{
Body = Part("application", "pkcs7-mime", smimeType: "enveloped-data"),
};
Assert.Multiple(() =>
{
Assert.That(MailEncryptionDetection.Detect((BodyPart?)null), Is.EqualTo(MailEncryptionKind.NONE));
Assert.That(MailEncryptionDetection.Detect(Multipart("alternative", PLAIN_TEXT, Part("text", "html"))), Is.EqualTo(MailEncryptionKind.NONE));
Assert.That(MailEncryptionDetection.Detect(Multipart("signed", PLAIN_TEXT, Part("application", "pkcs7-signature", name: "smime.p7s"))), Is.EqualTo(MailEncryptionKind.NONE), "A mail signed in the clear counts as encrypted.");
Assert.That(MailEncryptionDetection.Detect(Multipart("mixed", PLAIN_TEXT, attachedEncryptedMail)), Is.EqualTo(MailEncryptionKind.NONE), "An attached encrypted mail makes the mail around it unreadable.");
});
}
[TestCase("-----BEGIN PGP MESSAGE-----\n\nhQEMA\n-----END PGP MESSAGE-----", ExpectedResult = true)]
[TestCase("
-----BEGIN PGP MESSAGE-----
hQEMA
", ExpectedResult = true)]
[TestCase("-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\nReadable text.", ExpectedResult = false)]
[TestCase("Nothing to see here.", ExpectedResult = false)]
[TestCase(null, ExpectedResult = false)]
public bool AnEncryptedPgpBlockIsFoundInTheText(string? text) => MailEncryptionDetection.ContainsInlinePgpMessage(text);
private static BodyPartBasic Part(string mediaType, string mediaSubtype, string? smimeType = null, string? name = null)
{
var contentType = new ContentType(mediaType, mediaSubtype);
if (smimeType is not null)
contentType.Parameters.Add("smime-type", smimeType);
if (name is not null)
contentType.Name = name;
return new BodyPartBasic(contentType, PART_SPECIFIER);
}
private static BodyPartMultipart Multipart(string mediaSubtype, params BodyPart[] parts)
{
var multipart = new BodyPartMultipart(new ContentType("multipart", mediaSubtype), PART_SPECIFIER);
foreach (var part in parts)
multipart.BodyParts.Add(part);
return multipart;
}
}