Skip to content

Commit

Permalink
Refactored ImapFolder's GetSubfolder(s), Check, and Status methods to…
Browse files Browse the repository at this point in the history
… split sync/async
  • Loading branch information
jstedfast committed Dec 22, 2023
1 parent 5e34b78 commit fcca1ad
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 56 deletions.
4 changes: 2 additions & 2 deletions MailKit/Net/Imap/ImapEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3571,7 +3571,7 @@ public IList<IMailFolder> GetFolders (FolderNamespace @namespace, StatusItems it
if (status) {
for (int i = 0; i < list.Count; i++) {
if (list[i].Exists)
list[i].StatusAsync (items, false, false, cancellationToken).GetAwaiter ().GetResult ();
list[i].Status (items, false, cancellationToken);
}
}

Expand Down Expand Up @@ -3604,7 +3604,7 @@ public async Task<IList<IMailFolder>> GetFoldersAsync (FolderNamespace @namespac
if (status) {
for (int i = 0; i < list.Count; i++) {
if (list[i].Exists)
await list[i].StatusAsync (items, true, false, cancellationToken).ConfigureAwait (false);
await list[i].StatusAsync (items, false, cancellationToken).ConfigureAwait (false);
}
}

Expand Down
204 changes: 150 additions & 54 deletions MailKit/Net/Imap/ImapFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1625,7 +1625,7 @@ public override async Task UnsubscribeAsync (CancellationToken cancellationToken
ProcessUnsubscribeResponse (ic);
}

async Task<IList<IMailFolder>> GetSubfoldersAsync (StatusItems items, bool subscribedOnly, bool doAsync, CancellationToken cancellationToken)
ImapCommand QueueGetSubfolders (StatusItems items, bool subscribedOnly, CancellationToken cancellationToken, out List<ImapFolder> list, out bool status)
{
CheckState (false, false);

Expand All @@ -1641,9 +1641,9 @@ async Task<IList<IMailFolder>> GetSubfoldersAsync (StatusItems items, bool subsc
pattern.Append (DirectorySeparator);
pattern.Append ('%');

var children = new List<IMailFolder> ();
var status = items != StatusItems.None;
var list = new List<ImapFolder> ();
status = items != StatusItems.None;
list = new List<ImapFolder> ();

var command = new StringBuilder ();
var returnsSubscribed = false;
var lsub = subscribedOnly;
Expand Down Expand Up @@ -1699,13 +1699,17 @@ async Task<IList<IMailFolder>> GetSubfoldersAsync (StatusItems items, bool subsc

Engine.QueueCommand (ic);

await Engine.RunAsync (ic, doAsync).ConfigureAwait (false);
return ic;
}

IList<IMailFolder> ProcessGetSubfoldersResponse (ImapCommand ic, List<ImapFolder> list, out bool unparented)
{
// Note: Due to the fact that folders can contain wildcards in them, we'll need to
// filter out any folders that are not children of this folder.
var prefix = FullName.Length > 0 ? FullName + DirectorySeparator : string.Empty;
prefix = ImapUtils.CanonicalizeMailboxName (prefix, DirectorySeparator);
var unparented = false;
var children = new List<IMailFolder> ();
unparented = false;

foreach (var folder in list) {
var canonicalFullName = ImapUtils.CanonicalizeMailboxName (folder.FullName, folder.DirectorySeparator);
Expand All @@ -1728,19 +1732,7 @@ async Task<IList<IMailFolder>> GetSubfoldersAsync (StatusItems items, bool subsc
ProcessResponseCodes (ic, null);

if (ic.Response != ImapCommandResponse.Ok)
throw ImapCommandException.Create (lsub ? "LSUB" : "LIST", ic);

// Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their
// parent folders now so that they are not left in an inconsistent state.
if (unparented)
await Engine.LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false);

if (status) {
for (int i = 0; i < children.Count; i++) {
if (children[i].Exists)
await ((ImapFolder) children[i]).StatusAsync (items, doAsync, false, cancellationToken).ConfigureAwait (false);
}
}
throw ImapCommandException.Create (ic.Lsub ? "LSUB" : "LIST", ic);

return children;
}
Expand Down Expand Up @@ -1778,7 +1770,25 @@ async Task<IList<IMailFolder>> GetSubfoldersAsync (StatusItems items, bool subsc
/// </exception>
public override IList<IMailFolder> GetSubfolders (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default)
{
return GetSubfoldersAsync (items, subscribedOnly, false, cancellationToken).GetAwaiter ().GetResult ();
var ic = QueueGetSubfolders (items, subscribedOnly, cancellationToken, out var list, out var status);

Engine.Run (ic);

var children = ProcessGetSubfoldersResponse (ic, list, out var unparented);

// Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their
// parent folders now so that they are not left in an inconsistent state.
if (unparented)
Engine.LookupParentFolders (list, cancellationToken);

if (status) {
for (int i = 0; i < children.Count; i++) {
if (children[i].Exists)
((ImapFolder) children[i]).Status (items, false, cancellationToken);
}
}

return children;
}

/// <summary>
Expand Down Expand Up @@ -1812,12 +1822,30 @@ public override IList<IMailFolder> GetSubfolders (StatusItems items, bool subscr
/// <exception cref="ImapCommandException">
/// The server replied with a NO or BAD response.
/// </exception>
public override Task<IList<IMailFolder>> GetSubfoldersAsync (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default)
public override async Task<IList<IMailFolder>> GetSubfoldersAsync (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default)
{
return GetSubfoldersAsync (items, subscribedOnly, true, cancellationToken);
var ic = QueueGetSubfolders (items, subscribedOnly, cancellationToken, out var list, out var status);

await Engine.RunAsync (ic).ConfigureAwait (false);

var children = ProcessGetSubfoldersResponse (ic, list, out var unparented);

// Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their
// parent folders now so that they are not left in an inconsistent state.
if (unparented)
await Engine.LookupParentFoldersAsync (list, cancellationToken).ConfigureAwait (false);

if (status) {
for (int i = 0; i < children.Count; i++) {
if (children[i].Exists)
await ((ImapFolder) children[i]).StatusAsync (items, false, cancellationToken).ConfigureAwait (false);
}
}

return children;
}

async Task<IMailFolder> GetSubfolderAsync (string name, bool doAsync, CancellationToken cancellationToken)
ImapCommand QueueGetSubfolder (string name, CancellationToken cancellationToken, out List<ImapFolder> list, out string fullName, out string encodedName, out ImapFolder folder)
{
if (name == null)
throw new ArgumentNullException (nameof (name));
Expand All @@ -1827,12 +1855,13 @@ async Task<IMailFolder> GetSubfolderAsync (string name, bool doAsync, Cancellati

CheckState (false, false);

var fullName = FullName.Length > 0 ? FullName + DirectorySeparator + name : name;
var encodedName = Engine.EncodeMailboxName (fullName);
List<ImapFolder> list;
fullName = FullName.Length > 0 ? FullName + DirectorySeparator + name : name;
encodedName = Engine.EncodeMailboxName (fullName);

if (Engine.TryGetCachedFolder (encodedName, out var folder))
return folder;
if (Engine.TryGetCachedFolder (encodedName, out folder)) {
list = null;
return null;
}

// Note: folder names can contain wildcards (including '*' and '%'), so replace '*' with '%'
// in order to reduce the list of folders returned by our LIST command.
Expand All @@ -1844,7 +1873,12 @@ async Task<IMailFolder> GetSubfolderAsync (string name, bool doAsync, Cancellati

Engine.QueueCommand (ic);

await Engine.RunAsync (ic, doAsync).ConfigureAwait (false);
return ic;
}

ImapFolder ProcessGetSubfolderResponse (ImapCommand ic, List<ImapFolder> list, string encodedName)
{
ImapFolder folder;

ProcessResponseCodes (ic, null);

Expand All @@ -1854,15 +1888,6 @@ async Task<IMailFolder> GetSubfolderAsync (string name, bool doAsync, Cancellati
if ((folder = ImapEngine.GetFolder (list, encodedName)) != null)
folder.ParentFolder = this;

if (list.Count > 1 || folder == null) {
// Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their
// parent folders now so that they are not left in an inconsistent state.
await Engine.LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false);
}

if (folder == null)
throw new FolderNotFoundException (fullName);

return folder;
}

Expand Down Expand Up @@ -1907,7 +1932,25 @@ async Task<IMailFolder> GetSubfolderAsync (string name, bool doAsync, Cancellati
/// </exception>
public override IMailFolder GetSubfolder (string name, CancellationToken cancellationToken = default)
{
return GetSubfolderAsync (name, false, cancellationToken).GetAwaiter ().GetResult ();
var ic = QueueGetSubfolder (name, cancellationToken, out var list, out var fullName, out var encodedName, out var folder);

if (ic == null)
return folder;

Engine.Run (ic);

folder = ProcessGetSubfolderResponse (ic, list, encodedName);

if (list.Count > 1 || folder == null) {
// Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their
// parent folders now so that they are not left in an inconsistent state.
Engine.LookupParentFolders (list, cancellationToken);
}

if (folder == null)
throw new FolderNotFoundException (fullName);

return folder;
}

/// <summary>
Expand Down Expand Up @@ -1949,19 +1992,38 @@ public override IMailFolder GetSubfolder (string name, CancellationToken cancell
/// <exception cref="ImapCommandException">
/// The server replied with a NO or BAD response.
/// </exception>
public override Task<IMailFolder> GetSubfolderAsync (string name, CancellationToken cancellationToken = default)
public override async Task<IMailFolder> GetSubfolderAsync (string name, CancellationToken cancellationToken = default)
{
return GetSubfolderAsync (name, true, cancellationToken);
var ic = QueueGetSubfolder (name, cancellationToken, out var list, out var fullName, out var encodedName, out var folder);

if (ic == null)
return folder;

await Engine.RunAsync (ic).ConfigureAwait (false);

folder = ProcessGetSubfolderResponse (ic, list, encodedName);

if (list.Count > 1 || folder == null) {
// Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their
// parent folders now so that they are not left in an inconsistent state.
await Engine.LookupParentFoldersAsync (list, cancellationToken).ConfigureAwait (false);
}

if (folder == null)
throw new FolderNotFoundException (fullName);

return folder;
}

async Task CheckAsync (bool doAsync, CancellationToken cancellationToken)
ImapCommand QueueCheck (CancellationToken cancellationToken)
{
CheckState (true, false);

var ic = Engine.QueueCommand (cancellationToken, this, "CHECK\r\n");

await Engine.RunAsync (ic, doAsync).ConfigureAwait (false);
return Engine.QueueCommand (cancellationToken, this, "CHECK\r\n");
}

void ProcessCheckResponse (ImapCommand ic)
{
ProcessResponseCodes (ic, null);

if (ic.Response != ImapCommandResponse.Ok)
Expand Down Expand Up @@ -2004,7 +2066,11 @@ async Task CheckAsync (bool doAsync, CancellationToken cancellationToken)
/// </exception>
public override void Check (CancellationToken cancellationToken = default)
{
CheckAsync (false, cancellationToken).GetAwaiter ().GetResult ();
var ic = QueueCheck (cancellationToken);

Engine.Run (ic);

ProcessCheckResponse (ic);
}

/// <summary>
Expand Down Expand Up @@ -2042,32 +2108,62 @@ public override void Check (CancellationToken cancellationToken = default)
/// <exception cref="ImapCommandException">
/// The server replied with a NO or BAD response.
/// </exception>
public override Task CheckAsync (CancellationToken cancellationToken = default)
public override async Task CheckAsync (CancellationToken cancellationToken = default)
{
return CheckAsync (true, cancellationToken);
var ic = QueueCheck (cancellationToken);

await Engine.RunAsync (ic).ConfigureAwait (false);

ProcessCheckResponse (ic);
}

internal async Task StatusAsync (StatusItems items, bool doAsync, bool throwNotFound, CancellationToken cancellationToken)
ImapCommand QueueStatus (StatusItems items, CancellationToken cancellationToken)
{
if ((Engine.Capabilities & ImapCapabilities.Status) == 0)
throw new NotSupportedException ("The IMAP server does not support the STATUS command.");

CheckState (false, false);

if (items == StatusItems.None)
return;
return null;

var command = string.Format ("STATUS %F ({0})\r\n", Engine.GetStatusQuery (items));
var ic = Engine.QueueCommand (cancellationToken, null, command, this);

await Engine.RunAsync (ic, doAsync).ConfigureAwait (false);
return Engine.QueueCommand (cancellationToken, null, command, this);
}

void ProcessStatusResponse (ImapCommand ic, bool throwNotFound)
{
ProcessResponseCodes (ic, this, throwNotFound);

if (ic.Response != ImapCommandResponse.Ok)
throw ImapCommandException.Create ("STATUS", ic);
}

internal void Status (StatusItems items, bool throwNotFound, CancellationToken cancellationToken)
{
var ic = QueueStatus (items, cancellationToken);

if (ic == null)
return;

Engine.Run (ic);

ProcessStatusResponse (ic, throwNotFound);
}

internal async Task StatusAsync (StatusItems items, bool throwNotFound, CancellationToken cancellationToken)
{
var ic = QueueStatus (items, cancellationToken);

if (ic == null)
return;

await Engine.RunAsync (ic).ConfigureAwait (false);

ProcessStatusResponse (ic, throwNotFound);
}

/// <summary>
/// Update the values of the specified items.
/// </summary>
Expand Down Expand Up @@ -2114,7 +2210,7 @@ internal async Task StatusAsync (StatusItems items, bool doAsync, bool throwNotF
/// </exception>
public override void Status (StatusItems items, CancellationToken cancellationToken = default)
{
StatusAsync (items, false, true, cancellationToken).GetAwaiter ().GetResult ();
Status (items, true, cancellationToken);
}

/// <summary>
Expand Down Expand Up @@ -2164,7 +2260,7 @@ public override void Status (StatusItems items, CancellationToken cancellationTo
/// </exception>
public override Task StatusAsync (StatusItems items, CancellationToken cancellationToken = default)
{
return StatusAsync (items, true, true, cancellationToken);
return StatusAsync (items, true, cancellationToken);
}

static void ParseAcl (ImapEngine engine, ImapCommand ic)
Expand Down

0 comments on commit fcca1ad

Please sign in to comment.