Skip to content

Commit

Permalink
Clean up for #63, part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
danzuep committed Jun 22, 2024
1 parent bb33f9f commit 4639d56
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 88 deletions.
16 changes: 11 additions & 5 deletions samples/WorkerServiceExample/Worker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public Worker(IServiceScopeFactory serviceScopeFactory, ILoggerFactory loggerFac
protected override async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
await ReceiveAsync(cancellationToken);
//await ReceiveAsync(cancellationToken);
//await TemplateSendAsync(1, cancellationToken);
//await SendAttachmentAsync(500);
//await ReceiveAsync(cancellationToken);
Expand Down Expand Up @@ -131,7 +131,7 @@ private async Task MoveTopOneToFolderAsync(IMailFolderClient mailFolderClient, s
var uniqueId = await mailFolderClient.MoveToAsync(messageSummary, destinationFolderFullName, cancellationToken);
//var destinationFolder = await mailFolderClient.GetFolderAsync([destinationFolderFullName], cancellationToken);
//var uniqueId = await messageSummary.MoveToAsync(destinationFolder, cancellationToken);
_logger.LogInformation($"Added mime message to {_imapReceiver} {destinationFolderFullName} folder as #{uniqueId}.");
_logger.LogInformation($"Moved {_imapReceiver} mime message {messageSummary.UniqueId} to {destinationFolderFullName} folder as #{uniqueId}.");
}

private async Task CreateFolderAndMoveTopOneAsync(string mailFolderFullName = "Processed", CancellationToken cancellationToken = default)
Expand All @@ -144,11 +144,17 @@ private async Task CreateFolderAndMoveTopOneAsync(string mailFolderFullName = "P
await MoveTopOneToFolderAsync(mailFolderClient, mailFolderFullName, cancellationToken);
}

private async Task GetMailFolderCacheAsync(string destinationFolderFullName = "INBOX", CancellationToken cancellationToken = default)
private async Task GetMailFolderCacheAsync(string mailFolderFullName = "Processed", CancellationToken cancellationToken = default)
{
using var mailFolderClient = _serviceScope.ServiceProvider.GetRequiredService<IMailFolderClient>();
var mailFolderCache = _serviceScope.ServiceProvider.GetRequiredService<IMailFolderCache>();
var folder = await mailFolderCache.GetMailFolderAsync(_imapReceiver, destinationFolderFullName, createIfMissing: true, cancellationToken);
_logger.LogInformation(folder?.FullName);
var folder = await mailFolderCache.GetMailFolderAsync(_imapReceiver, mailFolderFullName, createIfMissing: true, cancellationToken);
_logger.LogInformation(folder.FullName);
var messageSummary = await GetTopMessageSummaryAsync(cancellationToken);
var uniqueId = await mailFolderCache.MoveToAsync(_imapReceiver, messageSummary, mailFolderFullName, cancellationToken);
//var destinationFolder = await mailFolderClient.GetFolderAsync([destinationFolderFullName], cancellationToken);
//var uniqueId = await messageSummary.MoveToAsync(destinationFolder, cancellationToken);
_logger.LogInformation($"Moved {_imapReceiver} mime message #{messageSummary.UniqueId} to {mailFolderFullName} folder as #{uniqueId}.");
}

public async Task GetMailFolderAsync(string mailFolderName, CancellationToken cancellationToken = default)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ namespace MailKitSimplified.Receiver.Services
public interface IMailFolderCache
{
Task<IMailFolder> GetMailFolderAsync(IImapReceiver imapReceiver, string mailFolderFullName, bool createIfMissing = false, CancellationToken cancellationToken = default);
Task<UniqueId?> MoveToAsync(IImapReceiver imapReceiver, IMessageSummary messageSummary, string destinationFolderFullName, CancellationToken cancellationToken = default);
}
}
75 changes: 34 additions & 41 deletions source/MailKitSimplified.Receiver/Services/MailFolderCache.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MailKit;
Expand All @@ -14,6 +13,7 @@ namespace MailKitSimplified.Receiver.Services
{
public class MailFolderCache : IMailFolderCache
{
private static readonly string _inbox = "INBOX";
private CancellationTokenSource _cancellationTokenSource;
private readonly IMemoryCache _memoryCache;
private readonly IOptionsMonitor<MailboxOptions> _mailboxOptions;
Expand All @@ -35,36 +35,19 @@ public async Task<IMailFolder> GetMailFolderAsync(IImapReceiver imapReceiver, st
{
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var imapClient = await imapReceiver.ConnectAuthenticatedImapClientAsync(_cancellationTokenSource.Token);
int folderCount = await CacheAllMailFoldersAsync(imapReceiver, imapClient).ConfigureAwait(false);
if (folderCount == 0)
if (mailFolderFullName.Equals(_inbox, StringComparison.OrdinalIgnoreCase))
mailFolder = imapClient.Inbox;
else
{
var inbox = imapClient.Inbox;
var inboxKey = GetKey(imapReceiver, inbox.FullName);
CacheMailFolder(inboxKey, inbox);
}
if (createIfMissing && !_memoryCache.TryGetValue(key, out mailFolder))
{
var namespaceFolder = imapClient.PersonalNamespaces.FirstOrDefault()
?? imapClient.SharedNamespaces.FirstOrDefault()
?? imapClient.OtherNamespaces.FirstOrDefault();
var baseFolder = string.IsNullOrEmpty(namespaceFolder?.Path) ?
imapClient.Inbox : await imapClient.GetFolderAsync(namespaceFolder.Path);

//if (mailFolderFullName.Length >= baseFolder.FullName.Length &&
// mailFolderFullName.Substring(0, baseFolder.FullName.Length) == baseFolder.FullName)
// mailFolderFullName = mailFolderFullName.Substring(baseFolder.FullName.Length).Trim('/');

bool peekFolder = !baseFolder?.IsOpen ?? true;
_ = await imapReceiver.MailFolderClient.ConnectAsync(true, cancellationToken).ConfigureAwait(false);
mailFolder = await baseFolder.CreateAsync(mailFolderFullName, isMessageFolder: true, cancellationToken);
if (peekFolder)
await baseFolder.CloseAsync(expunge: false, cancellationToken).ConfigureAwait(false);

//mailFolder = await imapReceiver.MailFolderClient.GetOrCreateFolderAsync(mailFolderFullName, cancellationToken).ConfigureAwait(false);
if (mailFolder != null)
int folderCount = await CacheAllMailFoldersAsync(imapReceiver, imapClient).ConfigureAwait(false);
if (createIfMissing && !_memoryCache.TryGetValue(key, out mailFolder))
{
var createdKey = GetKey(imapReceiver, mailFolder.FullName);
CacheMailFolder(createdKey, mailFolder);
mailFolder = await imapReceiver.MailFolderClient.GetOrCreateFolderAsync(mailFolderFullName, cancellationToken).ConfigureAwait(false);
if (mailFolder != null)
{
var createdKey = GetKey(imapReceiver, mailFolder.FullName);
CacheMailFolder(createdKey, mailFolder);
}
}
}
}
Expand All @@ -90,31 +73,41 @@ private void CacheMailFolder(string key, IMailFolder mailFolder)
});
}

#if NET5_0_OR_GREATER
private async Task<int> AsyncCacheAllMailFoldersAsync(IImapReceiver imapReceiver, IImapClient imapClient)
private async Task<int> CacheAllMailFoldersAsync(IImapReceiver imapReceiver, IImapClient imapClient)
{
int folderCount = 0;
await foreach (var mailFolder in imapClient.AsyncGetAllSubfolders(_cancellationTokenSource.Token).ConfigureAwait(false))
var key = GetKey(imapReceiver, _inbox);
if (!_memoryCache.TryGetValue(key, out var _))
{
var key = GetKey(imapReceiver, mailFolder.FullName);
CacheMailFolder(key, mailFolder);
CacheMailFolder(key, imapClient.Inbox);
folderCount++;
}
return folderCount;
}
#endif
private async Task<int> CacheAllMailFoldersAsync(IImapReceiver imapReceiver, IImapClient imapClient)
{
int folderCount = 0;
foreach (var mailFolder in await imapClient.GetAllSubfoldersAsync(_cancellationTokenSource.Token).ConfigureAwait(false))
{
var key = GetKey(imapReceiver, mailFolder.FullName);
key = GetKey(imapReceiver, mailFolder.FullName);
CacheMailFolder(key, mailFolder);
folderCount++;
}
return folderCount;
}

public async Task<UniqueId?> MoveToAsync(IImapReceiver imapReceiver, IMessageSummary messageSummary, string destinationFolderFullName, CancellationToken cancellationToken = default)
{
if (imapReceiver == null || messageSummary == null || !messageSummary.UniqueId.IsValid)
return null;
var source = await GetMailFolderAsync(imapReceiver, messageSummary.Folder.FullName, createIfMissing: false, cancellationToken).ConfigureAwait(false);
var destination = await GetMailFolderAsync(imapReceiver, destinationFolderFullName, createIfMissing: true, cancellationToken).ConfigureAwait(false);

bool peekSourceFolder = !source.IsOpen;
if (!source.IsOpen || source.Access != FolderAccess.ReadWrite)
await source.OpenAsync(FolderAccess.ReadWrite, cancellationToken).ConfigureAwait(false);
var resultUid = await source.MoveToAsync(messageSummary.UniqueId, destination, cancellationToken).ConfigureAwait(false);
if (peekSourceFolder && source.IsOpen)
await source.CloseAsync(expunge: false, cancellationToken).ConfigureAwait(false);

return resultUid;
}

// Let the Garbage Collector dispose of the injected MemoryCache.
}
}
86 changes: 44 additions & 42 deletions source/MailKitSimplified.Receiver/Services/MailFolderClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,25 +215,38 @@ public async Task<IMailFolder> GetFolderAsync(IEnumerable<string> folderNames, C
{
if (folderNames == null || !folderNames.Any())
throw new ArgumentNullException(nameof(folderNames));
IMailFolder folder = null;
IMailFolder mailFolder = null;
_semaphoreSlim.Wait();
try
{
var imapClient = await _imapReceiver.ConnectAuthenticatedImapClientAsync().ConfigureAwait(false);
#if NET5_0_OR_GREATER

var namespaceFolders = await imapClient.GetFoldersAsync(imapClient.PersonalNamespaces[0]).ConfigureAwait(false);
var namespaceSubfolders = await namespaceFolders[0].GetSubfoldersAsync(false, cancellationToken).ConfigureAwait(false);
#else
var namespaceSubfolders = await imapClient.GetAllSubfoldersAsync(cancellationToken).ConfigureAwait(false);
#endif
var mailFolder = namespaceSubfolders.FirstOrDefault(x => folderNames.Contains(x.Name, StringComparer.OrdinalIgnoreCase));
var imapClient = await _imapReceiver.ConnectAuthenticatedImapClientAsync(cancellationToken).ConfigureAwait(false);
if (folderNames.Contains("INBOX", StringComparer.OrdinalIgnoreCase))
{
mailFolder = imapClient.Inbox;
}
else
{
var folderNamespaces = Enumerable.Concat(Enumerable.Concat(imapClient.PersonalNamespaces, imapClient.SharedNamespaces), imapClient.OtherNamespaces);
foreach (var folderNamespace in folderNamespaces)
{
var folders = await imapClient.GetFoldersAsync(folderNamespace, subscribedOnly: true, cancellationToken).ConfigureAwait(false);
foreach (var mFolder in folders ?? Enumerable.Empty<IMailFolder>())
{
var subfolders = await mFolder.GetSubfoldersAsync(subscribedOnly: false, cancellationToken).ConfigureAwait(false);
mailFolder = subfolders.FirstOrDefault(x => folderNames.Contains(x.Name, StringComparer.OrdinalIgnoreCase));
if (cancellationToken.IsCancellationRequested || mailFolder != null)
break;
}
if (cancellationToken.IsCancellationRequested || mailFolder != null)
break;
}
}
}
finally
{
_semaphoreSlim.Release();
}
return folder;
return mailFolder;
}

public async Task<int> AddFlagsAsync(IEnumerable<UniqueId> uniqueIds, MessageFlags messageFlags, bool silent = true, CancellationToken cancellationToken = default)
Expand Down Expand Up @@ -286,31 +299,6 @@ public async Task<int> DeleteMessagesAsync(TimeSpan relativeOffset, SearchQuery
return await AddFlagsAsync(searchQuery, MessageFlags.Deleted, silent: true, cancellationToken).ConfigureAwait(false);
}

public async Task<UniqueId?> AppendSentMessageAsync(MimeMessage message, MessageFlags messageFlags = MessageFlags.Seen, CancellationToken cancellationToken = default, ITransferProgress transferProgress = default) =>
await SentFolder.AppendAsync(message, messageFlags, cancellationToken, transferProgress).ConfigureAwait(false);

public async Task<UniqueId?> CopyToAsync(IMessageSummary messageSummary, SpecialFolder mailFolder = SpecialFolder.Drafts, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageSummary.UniqueId, messageSummary.Folder, GetFolder(mailFolder).Value, move: false, cancellationToken).ConfigureAwait(false);

public async Task<UniqueId?> MoveToAsync(IMessageSummary messageSummary, SpecialFolder mailFolder = SpecialFolder.Sent, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageSummary.UniqueId, messageSummary.Folder, GetFolder(mailFolder).Value, move: true, cancellationToken).ConfigureAwait(false);

public async Task<UniqueId?> MoveToAsync(IMessageSummary messageSummary, string destinationFolderFullName, CancellationToken cancellationToken = default)
{
UniqueId? result;
if (_mailFolderCache == null)
{
result = await MoveOrCopyAsync(messageSummary.UniqueId, destinationFolderFullName, move: true, cancellationToken).ConfigureAwait(false);
}
else
{
var sourceFolder = await _mailFolderCache.GetMailFolderAsync(_imapReceiver, messageSummary.Folder.FullName, createIfMissing: false, cancellationToken).ConfigureAwait(false);
var destinationFolder = await _mailFolderCache.GetMailFolderAsync(_imapReceiver, destinationFolderFullName, createIfMissing: true, cancellationToken).ConfigureAwait(false);
result = await MoveOrCopyAsync(messageSummary.UniqueId, sourceFolder, destinationFolder, move: true, cancellationToken).ConfigureAwait(false);
}
return result;
}

internal async Task<UniqueId?> MoveOrCopyAsync(UniqueId messageUid, IMailFolder source, IMailFolder destination, bool move = true, CancellationToken cancellationToken = default)
{
UniqueId? resultUid = null;
Expand Down Expand Up @@ -410,23 +398,37 @@ private async Task<UniqueIdMap> MoveOrCopyAsync(IEnumerable<UniqueId> messageUid
return result ?? UniqueIdMap.Empty;
}

public async Task<UniqueId?> AppendSentMessageAsync(MimeMessage message, MessageFlags messageFlags = MessageFlags.Seen, CancellationToken cancellationToken = default, ITransferProgress transferProgress = default) =>
await SentFolder.AppendAsync(message, messageFlags, cancellationToken, transferProgress).ConfigureAwait(false);

public async Task<UniqueId?> CopyToAsync(IMessageSummary messageSummary, SpecialFolder mailFolder = SpecialFolder.Drafts, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageSummary.UniqueId, messageSummary.Folder, GetFolder(mailFolder).Value, move: false, cancellationToken).ConfigureAwait(false);

public async Task<UniqueId?> CopyToAsync(UniqueId messageUid, IMailFolder destination, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUid, _mailFolder, destination, move: false, cancellationToken).ConfigureAwait(false);

public async Task<UniqueId?> CopyToAsync(UniqueId messageUid, string destinationFolder, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUid, destinationFolder, move: false, cancellationToken).ConfigureAwait(false);
public async Task<UniqueId?> CopyToAsync(UniqueId messageUid, string destinationFolderFullName, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUid, destinationFolderFullName, move: false, cancellationToken).ConfigureAwait(false);

public async Task<UniqueIdMap> CopyToAsync(IEnumerable<UniqueId> messageUids, IMailFolder destination, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUids, destination, move: false, cancellationToken).ConfigureAwait(false);

public async Task<UniqueIdMap> CopyToAsync(IEnumerable<UniqueId> messageUids, string destinationFolder, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUids, destinationFolder, move: false, cancellationToken).ConfigureAwait(false);
public async Task<UniqueIdMap> CopyToAsync(IEnumerable<UniqueId> messageUids, string destinationFolderFullName, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUids, destinationFolderFullName, move: false, cancellationToken).ConfigureAwait(false);

public async Task<UniqueId?> MoveToAsync(UniqueId messageUid, IMailFolder destination, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUid, _mailFolder, destination, move: true, cancellationToken).ConfigureAwait(false);

public async Task<UniqueId?> MoveToAsync(UniqueId messageUid, string destinationFolder, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUid, destinationFolder, move: true, cancellationToken).ConfigureAwait(false);
public async Task<UniqueId?> MoveToAsync(UniqueId messageUid, string destinationFolderFullName, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUid, destinationFolderFullName, move: true, cancellationToken).ConfigureAwait(false);

[Obsolete("Use MoveToAsync with messageSummary.UniqueId instead.")]
public async Task<UniqueId?> MoveToAsync(IMessageSummary messageSummary, SpecialFolder mailFolder = SpecialFolder.Sent, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageSummary.UniqueId, messageSummary.Folder, GetFolder(mailFolder).Value, move: true, cancellationToken).ConfigureAwait(false);

[Obsolete("Use MoveToAsync with messageSummary.UniqueId instead.")]
public async Task<UniqueId?> MoveToAsync(IMessageSummary messageSummary, string destinationFolderFullName, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageSummary.UniqueId, destinationFolderFullName, move: true, cancellationToken).ConfigureAwait(false);

public async Task<UniqueIdMap> MoveToAsync(IEnumerable<UniqueId> messageUids, IMailFolder destination, CancellationToken cancellationToken = default) =>
await MoveOrCopyAsync(messageUids, destination, move: true, cancellationToken).ConfigureAwait(false);
Expand Down

0 comments on commit 4639d56

Please sign in to comment.