Reputation: 339
I use MailKit for creating a folder. This is simple, I can e.g. call CreateFolderAsync
(if I want to async way) on the parent folder.
public async Task<IMailFolder> GetOrCreateFolder(string folderName)
{
var rootFolder = await authenticatedClient.GetFolderAsync(imapClient.PersonalNamespaces[0].Path);
var existingFolders = await rootFolder.GetSubfoldersAsync();
var matchingFolder = existingFolders.FirstOrDefault(folder => folder.FullName == folderName);
if (matchingFolder == null)
{
matchingFolder = await rootFolder.CreateAsync(folderName, true);
}
return matchingFolder;
}
However, if I pass in a string like GetOrCreateFolder("folder/subfolder")
or GetOrCreateFolder("folder.subfolder")
(because I later found out many IMAP servers apparently use .
as a directory separator sign. This is defined/returned by the server in IMailFolder.DirectorySeparator
e.g.).
Both will throw an exception if you try this ("The name is not a legal folder name"):
System.ArgumentException
HResult=0x80070057
Nachricht = The name is not a legal folder name. Arg_ParamName_Name
Quelle = MailKit
Stapelüberwachung:
bei MailKit.Net.Imap.ImapFolder.QueueCreateCommand(String name, Boolean isMessageFolder, CancellationToken cancellationToken, String& encodedName)
bei MailKit.Net.Imap.ImapFolder.CreateAsync(String name, Boolean isMessageFolder, CancellationToken cancellationToken)
// ...
Diese Ausnahme wurde ursprünglich von dieser Aufrufliste ausgelöst:
MailKit.Net.Imap.ImapFolder.QueueCreateCommand(string, bool, System.Threading.CancellationToken, out string)
MailKit.Net.Imap.ImapFolder.CreateAsync(string, bool, System.Threading.CancellationToken)
You can obviously hardcode the creation of the first folder, and then the second, but this is really not flexible at all.
I mean, after all, mkdir
on Linux e.g. also supports creating multiple folders at once, why can't we here?
So, how can I just make this work?
Upvotes: 0
Views: 82
Reputation: 339
Actually, I have an answer where I just recursively create such a directory:
private const char directorySeparator = '/';
/// <summary>
/// Gets a folder or sub-folder (recursively). If needed, the folder(s) is/are automatically created.
/// </summary>
/// <param name="folderPath">The full path of the folder to create, separated by slashes (<see cref="directorySeparator"/>).</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task<IMailFolder> GetOrCreateFolder(string folderPath)
{
var rootFolder = await imapClient.GetFolderAsync(imapClient.PersonalNamespaces[0].Path);
return await GetOrCreateFolderRecursiveAsync(rootFolder, folderPath);
}
private async Task<IMailFolder> GetOrCreateFolderRecursiveAsync(IMailFolder parentFolder, string folderPath)
{
if (string.IsNullOrEmpty(folderPath) || folderPath == ".")
return parentFolder;
var (currentFolderName, remainingPath) = SplitCurrentFolderAndRemainingPath(folderPath, [directorySeparator, parentFolder.DirectorySeparator]);
IMailFolder matchingFolder;
try
{
matchingFolder = await parentFolder.GetSubfolderAsync(currentFolderName);
}
catch (FolderNotFoundException)
{
logger.LogInformation($"Creating folder {currentFolderName} under {parentFolder.FullName}.");
matchingFolder = await parentFolder.CreateAsync(currentFolderName, true);
}
return await GetOrCreateFolderRecursiveAsync(matchingFolder, remainingPath);
}
private static (string currentFolderName, string remainingPath) SplitCurrentFolderAndRemainingPath(string folderPath,
char[] separator)
{
var parts = folderPath.Split(separator, 2, StringSplitOptions.RemoveEmptyEntries);
var currentFolderName = parts[0];
var remainingPath = parts.Length > 1 ? parts[1] : string.Empty;
return (currentFolderName, remainingPath);
}
It just assumes you have an already authenticated imapClient
aka ImapClient
of course. The logger
is also optional.
It allows /
as a hardcoded directory separator or IMailFolder.DirectorySeparator
from the server, which I have mentioned before. That, AFAIK, usually is a .
aka dot.
AFAIK if IMailFolder.DirectorySeparator
appears in your folder, the server will reject it anyway, as shown above, so it does not hurt to split it here.
I only implemented the asynchronous way, because that's what I use, but obviously, the synchronous way would be equivalent.
Upvotes: 0
Reputation: 38653
Change your implementation to this:
public async Task<IMailFolder> GetOrCreateFolder(string folderName)
{
var rootFolder = await authenticatedClient.GetFolderAsync(imapClient.PersonalNamespaces[0]);
var path = folderName.Split(new char[] { rootFolder.DirectorySeparator }, folderName);
var folder = rootFolder;
int i = 0;
// Traverse down the path getting each node until one fails...
while (i < path.Count) {
try {
folder = await folder.GetSubfolderAsync(path[i]);
i++;
} catch (FolderNotFoundException) {
break;
}
}
// Create any remaining path nodes...
while (i < path.Count) {
folder = await folder.CreateAsync(path[i], i + 1 == path.Count);
i++;
}
return folder;
}
Upvotes: 1