Skip to content

Commit

Permalink
Refactored ImapFolder's Get/SetQuota methods to split sync/async
Browse files Browse the repository at this point in the history
Part of an ongoing effort to fix issue #1335
  • Loading branch information
jstedfast committed Dec 26, 2023
1 parent c32a2a0 commit 1e6253d
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 48 deletions.
63 changes: 39 additions & 24 deletions MailKit/Net/Imap/ImapEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3296,48 +3296,63 @@ public async Task QuerySpecialFoldersAsync (CancellationToken cancellationToken)
AssignSpecialFolders (list);
}

ImapFolder ProcessGetQuotaRootResponse (ImapCommand ic, string quotaRoot, out List<ImapFolder> list)
{
ImapFolder folder;

list = (List<ImapFolder>) ic.UserData;

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

if ((folder = GetFolder (list, quotaRoot)) == null) {
folder = CreateImapFolder (quotaRoot, FolderAttributes.NonExistent, '.');
CacheFolder (folder);
}

return folder;
}

/// <summary>
/// Gets the folder representing the specified quota root.
/// </summary>
/// <returns>The folder.</returns>
/// <param name="quotaRoot">The name of the quota root.</param>
/// <param name="doAsync">Whether or not asynchronous IO methods should be used.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public async Task<ImapFolder> GetQuotaRootFolderAsync (string quotaRoot, bool doAsync, CancellationToken cancellationToken)
public ImapFolder GetQuotaRootFolder (string quotaRoot, CancellationToken cancellationToken)
{
if (TryGetCachedFolder (quotaRoot, out var folder))
return folder;

var command = new StringBuilder ("LIST \"\" %S");
var list = new List<ImapFolder> ();
var returnsSubscribed = false;
var ic = QueueGetFolderCommand (quotaRoot, cancellationToken);

if ((Capabilities & ImapCapabilities.ListExtended) != 0) {
command.Append (" RETURN (SUBSCRIBED CHILDREN)");
returnsSubscribed = true;
}
Run (ic);

command.Append ("\r\n");
folder = ProcessGetQuotaRootResponse (ic, quotaRoot, out var list);

var ic = new ImapCommand (this, cancellationToken, null, command.ToString (), quotaRoot);
ic.RegisterUntaggedHandler ("LIST", ImapUtils.UntaggedListHandler);
ic.ListReturnsSubscribed = returnsSubscribed;
ic.UserData = list;
LookupParentFolders (list, cancellationToken);

QueueCommand (ic);
return folder;
}

/// <summary>
/// Gets the folder representing the specified quota root.
/// </summary>
/// <returns>The folder.</returns>
/// <param name="quotaRoot">The name of the quota root.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public async Task<ImapFolder> GetQuotaRootFolderAsync (string quotaRoot, CancellationToken cancellationToken)
{
if (TryGetCachedFolder (quotaRoot, out var folder))
return folder;

await RunAsync (ic, doAsync).ConfigureAwait (false);
var ic = QueueGetFolderCommand (quotaRoot, cancellationToken);

if (ic.Response != ImapCommandResponse.Ok)
throw ImapCommandException.Create ("LIST", ic);
await RunAsync (ic).ConfigureAwait (false);

if ((folder = GetFolder (list, quotaRoot)) == null) {
folder = CreateImapFolder (quotaRoot, FolderAttributes.NonExistent, '.');
CacheFolder (folder);
return folder;
}
folder = ProcessGetQuotaRootResponse (ic, quotaRoot, out var list);

await LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false);
await LookupParentFoldersAsync (list, cancellationToken).ConfigureAwait (false);

return folder;
}
Expand Down
87 changes: 63 additions & 24 deletions MailKit/Net/Imap/ImapFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3812,7 +3812,7 @@ static Task UntaggedQuotaHandler (ImapEngine engine, ImapCommand ic, int index,
return Task.CompletedTask;
}

async Task<FolderQuota> GetQuotaAsync (bool doAsync, CancellationToken cancellationToken)
ImapCommand QueueGetQuota (CancellationToken cancellationToken)
{
CheckState (false, false);

Expand All @@ -3828,31 +3828,29 @@ async Task<FolderQuota> GetQuotaAsync (bool doAsync, CancellationToken cancellat

Engine.QueueCommand (ic);

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

bool TryProcessGetQuotaResponse (ImapCommand ic, out string encodedName, out Quota quota)
{
var ctx = (QuotaContext) ic.UserData;

ProcessResponseCodes (ic, null);

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

for (int i = 0; i < ctx.QuotaRoots.Count; i++) {
var encodedName = ctx.QuotaRoots[i];
ImapFolder quotaRoot;

if (!ctx.Quotas.TryGetValue (encodedName, out var quota))
continue;
encodedName = ctx.QuotaRoots[i];

quotaRoot = await Engine.GetQuotaRootFolderAsync (encodedName, doAsync, cancellationToken).ConfigureAwait (false);

return new FolderQuota (quotaRoot) {
CurrentMessageCount = quota.CurrentMessageCount,
CurrentStorageSize = quota.CurrentStorageSize,
MessageLimit = quota.MessageLimit,
StorageLimit = quota.StorageLimit
};
if (ctx.Quotas.TryGetValue (encodedName, out quota))
return true;
}

return new FolderQuota (null);
encodedName = null;
quota = null;

return false;
}

/// <summary>
Expand Down Expand Up @@ -3891,7 +3889,21 @@ async Task<FolderQuota> GetQuotaAsync (bool doAsync, CancellationToken cancellat
/// </exception>
public override FolderQuota GetQuota (CancellationToken cancellationToken = default)
{
return GetQuotaAsync (false, cancellationToken).GetAwaiter ().GetResult ();
var ic = QueueGetQuota (cancellationToken);

Engine.Run (ic);

if (!TryProcessGetQuotaResponse (ic, out var encodedName, out var quota))
return new FolderQuota (null);

var quotaRoot = Engine.GetQuotaRootFolder (encodedName, cancellationToken);

return new FolderQuota (quotaRoot) {
CurrentMessageCount = quota.CurrentMessageCount,
CurrentStorageSize = quota.CurrentStorageSize,
MessageLimit = quota.MessageLimit,
StorageLimit = quota.StorageLimit
};
}

/// <summary>
Expand Down Expand Up @@ -3928,12 +3940,26 @@ public override FolderQuota GetQuota (CancellationToken cancellationToken = defa
/// <exception cref="ImapCommandException">
/// The server replied with a NO or BAD response.
/// </exception>
public override Task<FolderQuota> GetQuotaAsync (CancellationToken cancellationToken = default)
public override async Task<FolderQuota> GetQuotaAsync (CancellationToken cancellationToken = default)
{
return GetQuotaAsync (true, cancellationToken);
var ic = QueueGetQuota (cancellationToken);

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

if (!TryProcessGetQuotaResponse (ic, out var encodedName, out var quota))
return new FolderQuota (null);

var quotaRoot = await Engine.GetQuotaRootFolderAsync (encodedName, cancellationToken).ConfigureAwait (false);

return new FolderQuota (quotaRoot) {
CurrentMessageCount = quota.CurrentMessageCount,
CurrentStorageSize = quota.CurrentStorageSize,
MessageLimit = quota.MessageLimit,
StorageLimit = quota.StorageLimit
};
}

async Task<FolderQuota> SetQuotaAsync (uint? messageLimit, uint? storageLimit, bool doAsync, CancellationToken cancellationToken)
ImapCommand QueueSetQuota (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken)
{
CheckState (false, false);

Expand Down Expand Up @@ -3962,7 +3988,12 @@ async Task<FolderQuota> SetQuotaAsync (uint? messageLimit, uint? storageLimit, b

Engine.QueueCommand (ic);

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

FolderQuota ProcessSetQuotaResponse (ImapCommand ic)
{
var ctx = (QuotaContext) ic.UserData;

ProcessResponseCodes (ic, null);

Expand Down Expand Up @@ -4019,7 +4050,11 @@ async Task<FolderQuota> SetQuotaAsync (uint? messageLimit, uint? storageLimit, b
/// </exception>
public override FolderQuota SetQuota (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default)
{
return SetQuotaAsync (messageLimit, storageLimit, false, cancellationToken).GetAwaiter ().GetResult ();
var ic = QueueSetQuota (messageLimit, storageLimit, cancellationToken);

Engine.Run (ic);

return ProcessSetQuotaResponse (ic);
}

/// <summary>
Expand Down Expand Up @@ -4058,9 +4093,13 @@ public override FolderQuota SetQuota (uint? messageLimit, uint? storageLimit, Ca
/// <exception cref="ImapCommandException">
/// The server replied with a NO or BAD response.
/// </exception>
public override Task<FolderQuota> SetQuotaAsync (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default)
public override async Task<FolderQuota> SetQuotaAsync (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default)
{
return SetQuotaAsync (messageLimit, storageLimit, true, cancellationToken);
var ic = QueueSetQuota (messageLimit, storageLimit, cancellationToken);

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

return ProcessSetQuotaResponse (ic);
}

async Task ExpungeAsync (bool doAsync, CancellationToken cancellationToken)
Expand Down

0 comments on commit 1e6253d

Please sign in to comment.