Skip to content

Commit 0999c49

Browse files
committed
Add work-around for iCloud responses to ENABLE QRESYNC
Fixes issue #1871
1 parent 9415267 commit 0999c49

File tree

8 files changed

+125
-2
lines changed

8 files changed

+125
-2
lines changed

MailKit/Net/Imap/ImapClient.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,18 @@ bool TryQueueEnableQuickResyncCommand (CancellationToken cancellationToken, out
380380
return true;
381381
}
382382

383-
static void ProcessEnableResponse (ImapCommand ic)
383+
void ProcessEnableResponse (ImapCommand ic)
384384
{
385385
ic.ThrowIfNotOk ("ENABLE");
386+
387+
if (engine.QuirksMode == ImapQuirksMode.iCloud) {
388+
// Note: iCloud's response to the `ENABLE QRESYNC CONDSTORE` command does not include an untagged response
389+
// notifying us that QRESYNC or CONDSTORE have been enabled. Instead, if we get a tagged OK response, we
390+
// assume that these features were enabled successfully.
391+
//
392+
// See https://github.com/jstedfast/MailKit/issues/1871 for details.
393+
engine.QResyncEnabled = true;
394+
}
386395
}
387396

388397
/// <summary>

UnitTests/Net/Imap/ImapClientTests.cs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ 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 ICloudInitialCapabilities = ImapCapabilities.IMAP4 | ImapCapabilities.IMAP4rev1 |
75+
ImapCapabilities.Status | ImapCapabilities.SaslIR;
76+
static readonly ImapCapabilities ICloudAuthenticatedCapabilities = ImapCapabilities.IMAP4 | ImapCapabilities.IMAP4rev1 |
77+
ImapCapabilities.Status | ImapCapabilities.CondStore | ImapCapabilities.Enable | ImapCapabilities.QuickResync |
78+
ImapCapabilities.Quota | ImapCapabilities.Namespace | ImapCapabilities.UidPlus | ImapCapabilities.Children |
79+
ImapCapabilities.Binary | ImapCapabilities.Unselect | ImapCapabilities.Sort | ImapCapabilities.Catenate |
80+
ImapCapabilities.Language | ImapCapabilities.ESearch | ImapCapabilities.ESort | ImapCapabilities.Thread |
81+
ImapCapabilities.Context | ImapCapabilities.Within | ImapCapabilities.SaslIR | ImapCapabilities.SearchResults |
82+
ImapCapabilities.Metadata | ImapCapabilities.Id | ImapCapabilities.Annotate | ImapCapabilities.MultiSearch |
83+
ImapCapabilities.Idle | ImapCapabilities.ListStatus;
7484
static readonly ImapCapabilities IMAP4rev2CoreCapabilities = ImapCapabilities.IMAP4rev2 | ImapCapabilities.Status |
7585
ImapCapabilities.Namespace | ImapCapabilities.Unselect | ImapCapabilities.UidPlus | ImapCapabilities.ESearch |
7686
ImapCapabilities.SearchResults | ImapCapabilities.Enable | ImapCapabilities.Idle | ImapCapabilities.SaslIR | ImapCapabilities.ListExtended |
@@ -3564,6 +3574,8 @@ public void TestEnableQuickResync ()
35643574

35653575
client.EnableQuickResync ();
35663576

3577+
Assert.That (client.Inbox.Supports (FolderFeature.QuickResync), Is.True, "Expected the INBOX to support QRESYNC");
3578+
35673579
// ENABLE QRESYNC a second time should no-op.
35683580
client.EnableQuickResync ();
35693581

@@ -3606,6 +3618,100 @@ public async Task TestEnableQuickResyncAsync ()
36063618

36073619
await client.EnableQuickResyncAsync ();
36083620

3621+
Assert.That (client.Inbox.Supports (FolderFeature.QuickResync), Is.True, "Expected the INBOX to support QRESYNC");
3622+
3623+
// ENABLE QRESYNC a second time should no-op.
3624+
await client.EnableQuickResyncAsync ();
3625+
3626+
await client.DisconnectAsync (false);
3627+
}
3628+
}
3629+
3630+
static List<ImapReplayCommand> CreateEnableQuickResynciCloudCommands ()
3631+
{
3632+
return new List<ImapReplayCommand> {
3633+
new ImapReplayCommand ("", "icloud.greeting.txt"),
3634+
new ImapReplayCommand ("A00000000 AUTHENTICATE PLAIN AHVzZXJuYW1lAHBhc3N3b3Jk\r\n", "icloud.authenticate-plain.txt"),
3635+
new ImapReplayCommand ("A00000001 CAPABILITY\r\n", "icloud.capability.txt"),
3636+
new ImapReplayCommand ("A00000002 NAMESPACE\r\n", "icloud.namespace.txt"),
3637+
new ImapReplayCommand ("A00000003 LIST \"\" \"INBOX\"\r\n", "icloud.list-inbox.txt"),
3638+
new ImapReplayCommand ("A00000004 ENABLE QRESYNC CONDSTORE\r\n", "icloud.enable-qresync.txt"),
3639+
};
3640+
}
3641+
3642+
[Test]
3643+
public void TestEnableQuickResynciCloud ()
3644+
{
3645+
var commands = CreateEnableQuickResynciCloudCommands ();
3646+
3647+
using (var client = new ImapClient () { TagPrefix = 'A' }) {
3648+
try {
3649+
client.Connect (new ImapReplayStream (commands, false), "localhost", 143, SecureSocketOptions.None);
3650+
} catch (Exception ex) {
3651+
Assert.Fail ($"Did not expect an exception in Connect: {ex}");
3652+
}
3653+
3654+
Assert.That (client.Capabilities, Is.EqualTo (ICloudInitialCapabilities));
3655+
Assert.That (client.AuthenticationMechanisms, Has.Count.EqualTo (4));
3656+
Assert.That (client.AuthenticationMechanisms, Does.Contain ("ATOKEN"), "Expected SASL ATOKEN auth mechanism");
3657+
Assert.That (client.AuthenticationMechanisms, Does.Contain ("PLAIN"), "Expected SASL PLAIN auth mechanism");
3658+
Assert.That (client.AuthenticationMechanisms, Does.Contain ("ATOKEN2"), "Expected SASL ATOKEN2 auth mechanism");
3659+
Assert.That (client.AuthenticationMechanisms, Does.Contain ("XOAUTH2"), "Expected SASL XOAUTH2 auth mechanism");
3660+
3661+
try {
3662+
client.Authenticate ("username", "password");
3663+
} catch (Exception ex) {
3664+
Assert.Fail ($"Did not expect an exception in Authenticate: {ex}");
3665+
}
3666+
3667+
Assert.That (client.Capabilities, Is.EqualTo (ICloudAuthenticatedCapabilities));
3668+
Assert.That (client.ThreadingAlgorithms, Does.Contain (ThreadingAlgorithm.OrderedSubject), "Expected THREAD=ORDEREDSUBJECT");
3669+
Assert.That (client.ThreadingAlgorithms, Does.Contain (ThreadingAlgorithm.References), "Expected THREAD=REFERENCES");
3670+
3671+
client.EnableQuickResync ();
3672+
3673+
Assert.That (client.Inbox.Supports (FolderFeature.QuickResync), Is.True, "Expected the INBOX to support QRESYNC");
3674+
3675+
// ENABLE QRESYNC a second time should no-op.
3676+
client.EnableQuickResync ();
3677+
3678+
client.Disconnect (false);
3679+
}
3680+
}
3681+
3682+
[Test]
3683+
public async Task TestEnableQuickResynciCloudAsync ()
3684+
{
3685+
var commands = CreateEnableQuickResynciCloudCommands ();
3686+
3687+
using (var client = new ImapClient () { TagPrefix = 'A' }) {
3688+
try {
3689+
await client.ConnectAsync (new ImapReplayStream (commands, true), "localhost", 143, SecureSocketOptions.None);
3690+
} catch (Exception ex) {
3691+
Assert.Fail ($"Did not expect an exception in Connect: {ex}");
3692+
}
3693+
3694+
Assert.That (client.Capabilities, Is.EqualTo (ICloudInitialCapabilities));
3695+
Assert.That (client.AuthenticationMechanisms, Has.Count.EqualTo (4));
3696+
Assert.That (client.AuthenticationMechanisms, Does.Contain ("ATOKEN"), "Expected SASL ATOKEN auth mechanism");
3697+
Assert.That (client.AuthenticationMechanisms, Does.Contain ("PLAIN"), "Expected SASL PLAIN auth mechanism");
3698+
Assert.That (client.AuthenticationMechanisms, Does.Contain ("ATOKEN2"), "Expected SASL ATOKEN2 auth mechanism");
3699+
Assert.That (client.AuthenticationMechanisms, Does.Contain ("XOAUTH2"), "Expected SASL XOAUTH2 auth mechanism");
3700+
3701+
try {
3702+
await client.AuthenticateAsync ("username", "password");
3703+
} catch (Exception ex) {
3704+
Assert.Fail ($"Did not expect an exception in Authenticate: {ex}");
3705+
}
3706+
3707+
Assert.That (client.Capabilities, Is.EqualTo (ICloudAuthenticatedCapabilities));
3708+
Assert.That (client.ThreadingAlgorithms, Does.Contain (ThreadingAlgorithm.OrderedSubject), "Expected THREAD=ORDEREDSUBJECT");
3709+
Assert.That (client.ThreadingAlgorithms, Does.Contain (ThreadingAlgorithm.References), "Expected THREAD=REFERENCES");
3710+
3711+
await client.EnableQuickResyncAsync ();
3712+
3713+
Assert.That (client.Inbox.Supports (FolderFeature.QuickResync), Is.True, "Expected the INBOX to support QRESYNC");
3714+
36093715
// ENABLE QRESYNC a second time should no-op.
36103716
await client.EnableQuickResyncAsync ();
36113717

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
A######## OK user username authenticated
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* CAPABILITY XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 CONDSTORE ENABLE QRESYNC QUOTA XAPPLELITERAL NAMESPACE UIDPLUS CHILDREN BINARY UNSELECT SORT CATENATE URLAUTH LANGUAGE ESEARCH ESORT THREAD=ORDEREDSUBJECT THREAD=REFERENCES CONTEXT=SEARCH CONTEXT=SORT WITHIN SASL-IR SEARCHRES METADATA ID XMSEARCH X-SUN-SORT ANNOTATE-EXPERIMENT-1 X-UNAUTHENTICATE X-SUN-IMAP XUM1 MULTISEARCH IDLE X-APPLE-REMOTE-LINKS LIST-STATUS
2+
A######## OK Completed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
A######## OK ENABLE completed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* OK [CAPABILITY XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN] (2313B20-3ff771b405ab) pv50p00im-tygg10010601.me.com
1+
* OK [CAPABILITY XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN AUTH=ATOKEN2 AUTH=XOAUTH2] (2428B47-26126cfe23cd) p00-iscream-f9f7bf749-qjr87
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* LIST (\Noinferiors) "/" "INBOX"
2+
A######## OK LIST completed (took 0 ms)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* NAMESPACE (("" "/")) NIL NIL
2+
A######## OK NAMESPACE completed (took 0 ms)

0 commit comments

Comments
 (0)