Skip to content

Commit 8e6832d

Browse files
committedSep 15, 2023
Added detection for IMAP4rev2
1 parent dc06f92 commit 8e6832d

File tree

7 files changed

+10197
-3
lines changed

7 files changed

+10197
-3
lines changed
 

‎MailKit/Net/Imap/ImapCapabilities.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public enum ImapCapabilities : ulong {
5656
IMAP4rev1 = 1L << 1,
5757

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

‎MailKit/Net/Imap/ImapEngine.cs

+17-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ enum ImapEngineState {
7878
enum ImapProtocolVersion {
7979
Unknown,
8080
IMAP4,
81-
IMAP4rev1
81+
IMAP4rev1,
82+
IMAP4rev2,
8283
}
8384

8485
enum ImapUntaggedResult {
@@ -1330,6 +1331,8 @@ void ProcessCapabilityToken (string atom)
13301331
Capabilities |= ImapCapabilities.IMAP4;
13311332
} else if (atom.Equals ("IMAP4REV1", StringComparison.OrdinalIgnoreCase)) {
13321333
Capabilities |= ImapCapabilities.IMAP4rev1;
1334+
} else if (atom.Equals ("IMAP4REV2", StringComparison.OrdinalIgnoreCase)) {
1335+
Capabilities |= ImapCapabilities.IMAP4rev2;
13331336
} else if (atom.Equals ("STATUS", StringComparison.OrdinalIgnoreCase)) {
13341337
Capabilities |= ImapCapabilities.Status;
13351338
} else if (atom.Equals ("ACL", StringComparison.OrdinalIgnoreCase)) {
@@ -1448,7 +1451,19 @@ void ProcessCapabilityToken (string atom)
14481451

14491452
void StandardizeCapabilities ()
14501453
{
1451-
if ((Capabilities & ImapCapabilities.IMAP4rev1) != 0) {
1454+
if ((Capabilities & ImapCapabilities.IMAP4rev2) != 0) {
1455+
ProtocolVersion = ImapProtocolVersion.IMAP4rev2;
1456+
1457+
// Rfc9051, Appendix E defines the capabilities that IMAP4rev2 should be assumed to implement:
1458+
Capabilities |= ImapCapabilities.Status |
1459+
ImapCapabilities.Namespace | ImapCapabilities.Unselect | ImapCapabilities.UidPlus | ImapCapabilities.ESearch |
1460+
ImapCapabilities.SearchResults | ImapCapabilities.Enable | ImapCapabilities.Idle | ImapCapabilities.SaslIR | ImapCapabilities.ListExtended |
1461+
ImapCapabilities.ListStatus | ImapCapabilities.Move | ImapCapabilities.LiteralMinus | ImapCapabilities.SpecialUse;
1462+
1463+
// Note: IMAP4rev2 also supports the FETCH portion of the 'BINARY' extension but not the APPEND portion. Since
1464+
// we currently have no way to distinguish between them using the ImapCapabilities enum, we do not enable the
1465+
// ImapCapabilities.Binary extension flag.
1466+
} else if ((Capabilities & ImapCapabilities.IMAP4rev1) != 0) {
14521467
ProtocolVersion = ImapProtocolVersion.IMAP4rev1;
14531468
Capabilities |= ImapCapabilities.Status;
14541469
} else if ((Capabilities & ImapCapabilities.IMAP4) != 0) {

‎RFCs.md

+3
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,6 @@ The following IETF specifications define the IMAP, POP3 and SMTP protocols:
117117
* [8514](https://tools.ietf.org/html/rfc8514): Internet Message Access Protocol (IMAP) - SAVEDATE Extension
118118
* [8689](https://tools.ietf.org/html/rfc8689): SMTP Require TLS Option
119119
* [8970](https://tools.ietf.org/html/rfc8970): IMAP4 Extension: Message Preview Generation
120+
* [9051](https://tools.ietf.org/html/rfc9051): Internet Message Access Protocol (IMAP) - Version 4rev2
121+
* [9208](https://tools.ietf.org/html/rfc9208): IMAP QUOTA Extension (Obsoletes rfc2087)
122+
* [9394](https://tools.ietf.org/html/rfc9394): IMAP PARTIAL Extension for Paged SEARCH and FETCH

‎UnitTests/Net/Imap/ImapClientTests.cs

+45
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ public class ImapClientTests
7171
ImapCapabilities.ESearch | ImapCapabilities.Compress | ImapCapabilities.Enable | ImapCapabilities.ListExtended |
7272
ImapCapabilities.ListStatus | ImapCapabilities.Move | ImapCapabilities.UTF8Accept | ImapCapabilities.XList |
7373
ImapCapabilities.GMailExt1 | ImapCapabilities.LiteralMinus | ImapCapabilities.AppendLimit;
74+
static readonly ImapCapabilities IMAP4rev2CoreCapabilities = ImapCapabilities.IMAP4rev2 | ImapCapabilities.Status |
75+
ImapCapabilities.Namespace | ImapCapabilities.Unselect | ImapCapabilities.UidPlus | ImapCapabilities.ESearch |
76+
ImapCapabilities.SearchResults | ImapCapabilities.Enable | ImapCapabilities.Idle | ImapCapabilities.SaslIR | ImapCapabilities.ListExtended |
77+
ImapCapabilities.ListStatus | ImapCapabilities.Move | ImapCapabilities.LiteralMinus | ImapCapabilities.SpecialUse;
7478
static readonly ImapCapabilities AclInitialCapabilities = GMailInitialCapabilities | ImapCapabilities.Acl;
7579
static readonly ImapCapabilities AclAuthenticatedCapabilities = GMailAuthenticatedCapabilities | ImapCapabilities.Acl;
7680
static readonly ImapCapabilities MetadataInitialCapabilities = GMailInitialCapabilities | ImapCapabilities.Metadata;
@@ -230,6 +234,47 @@ public void TestArgumentExceptions ()
230234
}
231235
}
232236

237+
static IList<ImapReplayCommand> CreateIMAP4rev2Commands ()
238+
{
239+
return new List<ImapReplayCommand> {
240+
new ImapReplayCommand ("", Encoding.ASCII.GetBytes ("* OK [CAPABILITY STARTTLS AUTH=SCRAM-SHA-256 LOGINDISABLED IMAP4rev2] IMAP4rev2 Service Ready\r\n")),
241+
};
242+
}
243+
244+
[Test]
245+
public void TestIMAP4rev2 ()
246+
{
247+
var commands = CreateIMAP4rev2Commands ();
248+
249+
using (var client = new ImapClient () { TagPrefix = 'A' }) {
250+
try {
251+
client.Connect (new ImapReplayStream (commands, false), "localhost", 143, SecureSocketOptions.None);
252+
} catch (Exception ex) {
253+
Assert.Fail ("Did not expect an exception in Connect: {0}", ex);
254+
}
255+
256+
Assert.AreEqual (IMAP4rev2CoreCapabilities | ImapCapabilities.StartTLS | ImapCapabilities.LoginDisabled, client.Capabilities, "Capabilities");
257+
Assert.IsTrue (client.AuthenticationMechanisms.Contains ("SCRAM-SHA-256"), "AUTH=SCRAM-SHA-256");
258+
}
259+
}
260+
261+
[Test]
262+
public async Task TestIMAP4rev2Async ()
263+
{
264+
var commands = CreateIMAP4rev2Commands ();
265+
266+
using (var client = new ImapClient () { TagPrefix = 'A' }) {
267+
try {
268+
await client.ConnectAsync (new ImapReplayStream (commands, true), "localhost", 143, SecureSocketOptions.None);
269+
} catch (Exception ex) {
270+
Assert.Fail ("Did not expect an exception in Connect: {0}", ex);
271+
}
272+
273+
Assert.AreEqual (IMAP4rev2CoreCapabilities | ImapCapabilities.StartTLS | ImapCapabilities.LoginDisabled, client.Capabilities, "Capabilities");
274+
Assert.IsTrue (client.AuthenticationMechanisms.Contains ("SCRAM-SHA-256"), "AUTH=SCRAM-SHA-256");
275+
}
276+
}
277+
233278
[Test]
234279
public void TestEscapeUserName ()
235280
{

‎rfc/rfc9051.txt

+8,659
Large diffs are not rendered by default.

‎rfc/rfc9208.txt

+1,001
Large diffs are not rendered by default.

‎rfc/rfc9394.txt

+471
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.