From f60a10cd1ee9e7f59ccd2ba3c711ecd06d3b5613 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 29 Dec 2023 22:27:45 -0500 Subject: [PATCH] Updated ImapEngine.cs to split UpdateNamespaces sync/async logic --- MailKit/Net/Imap/ImapEngine.cs | 121 +++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 13 deletions(-) diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index 01b03fbb33..3d3be34771 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -1501,7 +1501,7 @@ Task UpdateCapabilitiesAsync (ImapTokenType sentinel, bool doAsync, Cancellation return Task.CompletedTask; } - async ValueTask UpdateNamespacesAsync (bool doAsync, CancellationToken cancellationToken) + void UpdateNamespaces (CancellationToken cancellationToken) { var namespaces = new List { PersonalNamespaces, OtherNamespaces, SharedNamespaces @@ -1515,23 +1515,23 @@ async ValueTask UpdateNamespacesAsync (bool doAsync, CancellationToken cancellat SharedNamespaces.Clear (); OtherNamespaces.Clear (); - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); do { if (token.Type == ImapTokenType.OpenParen) { // parse the list of namespace pairs... - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); while (token.Type == ImapTokenType.OpenParen) { // parse the namespace pair - first token is the path - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); path = (string) token.Value; // second token is the directory separator - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); AssertToken (token, ImapTokenType.QString, ImapTokenType.Nil, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); @@ -1556,7 +1556,7 @@ async ValueTask UpdateNamespacesAsync (bool doAsync, CancellationToken cancellat folder.UpdateIsNamespace (true); do { - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); if (token.Type == ImapTokenType.CloseParen) break; @@ -1565,12 +1565,12 @@ async ValueTask UpdateNamespacesAsync (bool doAsync, CancellationToken cancellat AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); AssertToken (token, ImapTokenType.OpenParen, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); do { - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); if (token.Type == ImapTokenType.CloseParen) break; @@ -1580,7 +1580,7 @@ async ValueTask UpdateNamespacesAsync (bool doAsync, CancellationToken cancellat } while (true); // read the next token - it should either be '(' or ')' - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); } AssertToken (token, ImapTokenType.CloseParen, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); @@ -1588,12 +1588,107 @@ async ValueTask UpdateNamespacesAsync (bool doAsync, CancellationToken cancellat AssertToken (token, ImapTokenType.Nil, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); } - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); n++; } while (n < 3); while (token.Type != ImapTokenType.Eoln) - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + token = ReadToken (cancellationToken); + } + + async ValueTask UpdateNamespacesAsync (CancellationToken cancellationToken) + { + var namespaces = new List { + PersonalNamespaces, OtherNamespaces, SharedNamespaces + }; + ImapToken token; + string path; + char delim; + int n = 0; + + PersonalNamespaces.Clear (); + SharedNamespaces.Clear (); + OtherNamespaces.Clear (); + + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); + + do { + if (token.Type == ImapTokenType.OpenParen) { + // parse the list of namespace pairs... + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); + + while (token.Type == ImapTokenType.OpenParen) { + // parse the namespace pair - first token is the path + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); + + AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); + + path = (string) token.Value; + + // second token is the directory separator + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); + + AssertToken (token, ImapTokenType.QString, ImapTokenType.Nil, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); + + var qstring = token.Type == ImapTokenType.Nil ? string.Empty : (string) token.Value; + + if (qstring.Length > 0) { + delim = qstring[0]; + + // canonicalize the namespace path + path = path.TrimEnd (delim); + } else { + delim = '\0'; + } + + namespaces[n].Add (new FolderNamespace (delim, DecodeMailboxName (path))); + + if (!TryGetCachedFolder (path, out var folder)) { + folder = CreateImapFolder (path, FolderAttributes.None, delim); + CacheFolder (folder); + } + + folder.UpdateIsNamespace (true); + + do { + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); + + if (token.Type == ImapTokenType.CloseParen) + break; + + // NAMESPACE extension + + AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); + + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); + + AssertToken (token, ImapTokenType.OpenParen, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); + + do { + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); + + if (token.Type == ImapTokenType.CloseParen) + break; + + AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); + } while (true); + } while (true); + + // read the next token - it should either be '(' or ')' + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); + } + + AssertToken (token, ImapTokenType.CloseParen, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); + } else { + AssertToken (token, ImapTokenType.Nil, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); + } + + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); + n++; + } while (n < 3); + + while (token.Type != ImapTokenType.Eoln) + token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); } void ProcessResponseCodes (ImapCommand ic) @@ -2382,7 +2477,7 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) AssertToken (token, ImapTokenType.Eoln, GenericUntaggedResponseSyntaxErrorFormat, atom, token); } else if (atom.Equals ("NAMESPACE", StringComparison.OrdinalIgnoreCase)) { - UpdateNamespacesAsync (false, cancellationToken).GetAwaiter ().GetResult (); + UpdateNamespaces (cancellationToken); } else if (atom.Equals ("STATUS", StringComparison.OrdinalIgnoreCase)) { UpdateStatus (cancellationToken); } else if (IsOkNoOrBad (atom, out var result)) { @@ -2535,7 +2630,7 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation AssertToken (token, ImapTokenType.Eoln, GenericUntaggedResponseSyntaxErrorFormat, atom, token); } else if (atom.Equals ("NAMESPACE", StringComparison.OrdinalIgnoreCase)) { - await UpdateNamespacesAsync (doAsync: true, cancellationToken).ConfigureAwait (false); + await UpdateNamespacesAsync (cancellationToken).ConfigureAwait (false); } else if (atom.Equals ("STATUS", StringComparison.OrdinalIgnoreCase)) { await UpdateStatusAsync (cancellationToken).ConfigureAwait (false); } else if (IsOkNoOrBad (atom, out var result)) {