Skip to content

Commit

Permalink
Added detection for IMAP4rev2
Browse files Browse the repository at this point in the history
  • Loading branch information
jstedfast committed Sep 15, 2023
1 parent dc06f92 commit 8e6832d
Show file tree
Hide file tree
Showing 7 changed files with 10,197 additions and 3 deletions.
2 changes: 1 addition & 1 deletion MailKit/Net/Imap/ImapCapabilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public enum ImapCapabilities : ulong {
IMAP4rev1 = 1L << 1,

/// <summary>
/// The server implements the core IMAP4rev2 commands.
/// The server implements the core IMAP4rev2 commands described in <a href="https://tools.ietf.org/html/rfc9051">rfc9051</a>.
/// </summary>
IMAP4rev2 = 1L << 2,

Expand Down
19 changes: 17 additions & 2 deletions MailKit/Net/Imap/ImapEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ enum ImapEngineState {
enum ImapProtocolVersion {
Unknown,
IMAP4,
IMAP4rev1
IMAP4rev1,
IMAP4rev2,
}

enum ImapUntaggedResult {
Expand Down Expand Up @@ -1330,6 +1331,8 @@ void ProcessCapabilityToken (string atom)
Capabilities |= ImapCapabilities.IMAP4;
} else if (atom.Equals ("IMAP4REV1", StringComparison.OrdinalIgnoreCase)) {
Capabilities |= ImapCapabilities.IMAP4rev1;
} else if (atom.Equals ("IMAP4REV2", StringComparison.OrdinalIgnoreCase)) {
Capabilities |= ImapCapabilities.IMAP4rev2;
} else if (atom.Equals ("STATUS", StringComparison.OrdinalIgnoreCase)) {
Capabilities |= ImapCapabilities.Status;
} else if (atom.Equals ("ACL", StringComparison.OrdinalIgnoreCase)) {
Expand Down Expand Up @@ -1448,7 +1451,19 @@ void ProcessCapabilityToken (string atom)

void StandardizeCapabilities ()
{
if ((Capabilities & ImapCapabilities.IMAP4rev1) != 0) {
if ((Capabilities & ImapCapabilities.IMAP4rev2) != 0) {
ProtocolVersion = ImapProtocolVersion.IMAP4rev2;

// Rfc9051, Appendix E defines the capabilities that IMAP4rev2 should be assumed to implement:
Capabilities |= ImapCapabilities.Status |
ImapCapabilities.Namespace | ImapCapabilities.Unselect | ImapCapabilities.UidPlus | ImapCapabilities.ESearch |
ImapCapabilities.SearchResults | ImapCapabilities.Enable | ImapCapabilities.Idle | ImapCapabilities.SaslIR | ImapCapabilities.ListExtended |
ImapCapabilities.ListStatus | ImapCapabilities.Move | ImapCapabilities.LiteralMinus | ImapCapabilities.SpecialUse;

// Note: IMAP4rev2 also supports the FETCH portion of the 'BINARY' extension but not the APPEND portion. Since
// we currently have no way to distinguish between them using the ImapCapabilities enum, we do not enable the
// ImapCapabilities.Binary extension flag.
} else if ((Capabilities & ImapCapabilities.IMAP4rev1) != 0) {
ProtocolVersion = ImapProtocolVersion.IMAP4rev1;
Capabilities |= ImapCapabilities.Status;
} else if ((Capabilities & ImapCapabilities.IMAP4) != 0) {
Expand Down
3 changes: 3 additions & 0 deletions RFCs.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,6 @@ The following IETF specifications define the IMAP, POP3 and SMTP protocols:
* [8514](https://tools.ietf.org/html/rfc8514): Internet Message Access Protocol (IMAP) - SAVEDATE Extension
* [8689](https://tools.ietf.org/html/rfc8689): SMTP Require TLS Option
* [8970](https://tools.ietf.org/html/rfc8970): IMAP4 Extension: Message Preview Generation
* [9051](https://tools.ietf.org/html/rfc9051): Internet Message Access Protocol (IMAP) - Version 4rev2
* [9208](https://tools.ietf.org/html/rfc9208): IMAP QUOTA Extension (Obsoletes rfc2087)
* [9394](https://tools.ietf.org/html/rfc9394): IMAP PARTIAL Extension for Paged SEARCH and FETCH
45 changes: 45 additions & 0 deletions UnitTests/Net/Imap/ImapClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public class ImapClientTests
ImapCapabilities.ESearch | ImapCapabilities.Compress | ImapCapabilities.Enable | ImapCapabilities.ListExtended |
ImapCapabilities.ListStatus | ImapCapabilities.Move | ImapCapabilities.UTF8Accept | ImapCapabilities.XList |
ImapCapabilities.GMailExt1 | ImapCapabilities.LiteralMinus | ImapCapabilities.AppendLimit;
static readonly ImapCapabilities IMAP4rev2CoreCapabilities = ImapCapabilities.IMAP4rev2 | ImapCapabilities.Status |
ImapCapabilities.Namespace | ImapCapabilities.Unselect | ImapCapabilities.UidPlus | ImapCapabilities.ESearch |
ImapCapabilities.SearchResults | ImapCapabilities.Enable | ImapCapabilities.Idle | ImapCapabilities.SaslIR | ImapCapabilities.ListExtended |
ImapCapabilities.ListStatus | ImapCapabilities.Move | ImapCapabilities.LiteralMinus | ImapCapabilities.SpecialUse;
static readonly ImapCapabilities AclInitialCapabilities = GMailInitialCapabilities | ImapCapabilities.Acl;
static readonly ImapCapabilities AclAuthenticatedCapabilities = GMailAuthenticatedCapabilities | ImapCapabilities.Acl;
static readonly ImapCapabilities MetadataInitialCapabilities = GMailInitialCapabilities | ImapCapabilities.Metadata;
Expand Down Expand Up @@ -230,6 +234,47 @@ public void TestArgumentExceptions ()
}
}

static IList<ImapReplayCommand> CreateIMAP4rev2Commands ()
{
return new List<ImapReplayCommand> {
new ImapReplayCommand ("", Encoding.ASCII.GetBytes ("* OK [CAPABILITY STARTTLS AUTH=SCRAM-SHA-256 LOGINDISABLED IMAP4rev2] IMAP4rev2 Service Ready\r\n")),
};
}

[Test]
public void TestIMAP4rev2 ()
{
var commands = CreateIMAP4rev2Commands ();

using (var client = new ImapClient () { TagPrefix = 'A' }) {
try {
client.Connect (new ImapReplayStream (commands, false), "localhost", 143, SecureSocketOptions.None);
} catch (Exception ex) {
Assert.Fail ("Did not expect an exception in Connect: {0}", ex);
}

Assert.AreEqual (IMAP4rev2CoreCapabilities | ImapCapabilities.StartTLS | ImapCapabilities.LoginDisabled, client.Capabilities, "Capabilities");
Assert.IsTrue (client.AuthenticationMechanisms.Contains ("SCRAM-SHA-256"), "AUTH=SCRAM-SHA-256");
}
}

[Test]
public async Task TestIMAP4rev2Async ()
{
var commands = CreateIMAP4rev2Commands ();

using (var client = new ImapClient () { TagPrefix = 'A' }) {
try {
await client.ConnectAsync (new ImapReplayStream (commands, true), "localhost", 143, SecureSocketOptions.None);
} catch (Exception ex) {
Assert.Fail ("Did not expect an exception in Connect: {0}", ex);
}

Assert.AreEqual (IMAP4rev2CoreCapabilities | ImapCapabilities.StartTLS | ImapCapabilities.LoginDisabled, client.Capabilities, "Capabilities");
Assert.IsTrue (client.AuthenticationMechanisms.Contains ("SCRAM-SHA-256"), "AUTH=SCRAM-SHA-256");
}
}

[Test]
public void TestEscapeUserName ()
{
Expand Down
Loading

0 comments on commit 8e6832d

Please sign in to comment.