Add methods for fetching email UIDs and emails by UID
Added `FetchEmailUidsAsync` to `IImapEmailService` for retrieving UIDs of emails matching a filter, optimizing scenarios where only identifiers are needed. Added `FetchEmailByUidAsync` to fetch a single email by UID, with optional attachment handling. Implemented `FetchEmailUidsAsync` in `LimilabsImapEmailService` to connect to the IMAP server, construct search criteria, retrieve UIDs, and handle sorting and result limits. Added robust error handling for authentication and other failures. Implemented `FetchEmailByUidAsync` in `LimilabsImapEmailService` to fetch email content and flags for a specific UID, map the data to `ReceivedEmailContext`, and handle errors with logging for non-critical failures.
This commit is contained in:
@@ -19,6 +19,24 @@ public interface IImapEmailService
|
|||||||
MailSearchFilter filter,
|
MailSearchFilter filter,
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetches only the UIDs of messages matching the specified filter.
|
||||||
|
/// </summary>
|
||||||
|
Task<IEnumerable<long>> FetchEmailUidsAsync(
|
||||||
|
EmailAccountDto account,
|
||||||
|
MailSearchFilter filter,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetches a single email by its UID.
|
||||||
|
/// </summary>
|
||||||
|
Task<ReceivedEmailContext?> FetchEmailByUidAsync(
|
||||||
|
EmailAccountDto account,
|
||||||
|
long uid,
|
||||||
|
string folder = "INBOX",
|
||||||
|
bool withAttachments = false,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marks a message as seen (read) on the server.
|
/// Marks a message as seen (read) on the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -125,6 +125,115 @@ public class LimilabsImapEmailService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<long>> FetchEmailUidsAsync(
|
||||||
|
EmailAccountDto account,
|
||||||
|
MailSearchFilter filter,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
using var imap = new Imap();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAndAuthenticateAsync(imap, account);
|
||||||
|
await SelectFolderAsync(imap, filter.Folder);
|
||||||
|
|
||||||
|
List<ICriterion> criterions = [];
|
||||||
|
|
||||||
|
if (filter.UnseenOnly)
|
||||||
|
criterions.Add(Expression.HasFlag(Flag.Unseen));
|
||||||
|
|
||||||
|
if (filter.SubjectContains is not null)
|
||||||
|
criterions.Add(Expression.Subject(filter.SubjectContains));
|
||||||
|
|
||||||
|
if (filter.SenderContains is not null)
|
||||||
|
criterions.Add(Expression.From(filter.SenderContains));
|
||||||
|
|
||||||
|
if (filter.RecipientContains is not null)
|
||||||
|
criterions.Add(Expression.To(filter.RecipientContains));
|
||||||
|
|
||||||
|
if (filter.BodyContains is not null)
|
||||||
|
criterions.Add(Expression.Body(filter.BodyContains));
|
||||||
|
|
||||||
|
if (filter.Uid is UidFilter uidF)
|
||||||
|
{
|
||||||
|
if (uidF.Absolute is long exactUid)
|
||||||
|
criterions.Add(Expression.UID(new Limilabs.Client.IMAP.Range(exactUid, exactUid)));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long lo = uidF.Min ?? 1L;
|
||||||
|
long? hi = uidF.Max;
|
||||||
|
criterions.Add(Expression.UID(new Limilabs.Client.IMAP.Range(lo, hi)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.Date is DateFilter dateF)
|
||||||
|
{
|
||||||
|
if (dateF.After is DateTime after)
|
||||||
|
criterions.Add(Expression.SentSince(after.Date));
|
||||||
|
|
||||||
|
if (dateF.Before is DateTime before)
|
||||||
|
criterions.Add(Expression.SentBefore(before.Date.AddDays(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchExpression = criterions.Count > 0 ? Expression.And([.. criterions]) : Expression.All();
|
||||||
|
List<long> uids = [.. await imap.SearchAsync(searchExpression, cancellationToken)];
|
||||||
|
|
||||||
|
if (filter.SortOrder == MailSortOrder.NewestFirst)
|
||||||
|
uids.Reverse();
|
||||||
|
|
||||||
|
if (filter.MaxCount > 0 && uids.Count > filter.MaxCount)
|
||||||
|
uids = [.. uids.Take(filter.MaxCount)];
|
||||||
|
|
||||||
|
await imap.CloseAsync(cancellationToken);
|
||||||
|
return uids;
|
||||||
|
}
|
||||||
|
catch (Limilabs.Client.ServerException ex)
|
||||||
|
{
|
||||||
|
await imap.CloseSafelyAsync();
|
||||||
|
throw new AuthenticationFailedException(
|
||||||
|
$"IMAP authentication failed for account '{account.Username}'.", ex);
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||||
|
{
|
||||||
|
await imap.CloseSafelyAsync();
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Failed to fetch email UIDs from IMAP server '{account.ImapServer}'.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ReceivedEmailContext?> FetchEmailByUidAsync(
|
||||||
|
EmailAccountDto account,
|
||||||
|
long uid,
|
||||||
|
string folder = "INBOX",
|
||||||
|
bool withAttachments = false,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
using var imap = new Imap();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAndAuthenticateAsync(imap, account);
|
||||||
|
await SelectFolderAsync(imap, folder);
|
||||||
|
|
||||||
|
var eml = await imap.PeekMessageByUIDAsync(uid, cancellationToken);
|
||||||
|
var mail = new MailBuilder().CreateFromEml(eml);
|
||||||
|
var flags = await imap.GetFlagsByUIDAsync(uid, cancellationToken);
|
||||||
|
|
||||||
|
await imap.CloseAsync(cancellationToken);
|
||||||
|
return MapToContext(uid, mail, flags, withAttachments);
|
||||||
|
}
|
||||||
|
catch (Limilabs.Client.ServerException ex)
|
||||||
|
{
|
||||||
|
await imap.CloseSafelyAsync();
|
||||||
|
throw new AuthenticationFailedException(
|
||||||
|
$"IMAP authentication failed for account '{account.Username}'.", ex);
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||||
|
{
|
||||||
|
await imap.CloseSafelyAsync();
|
||||||
|
logger.LogWarning(ex, "Failed to fetch IMAP message UID={Uid} from folder {Folder}.", uid, folder);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task MarkAsSeenAsync(
|
public async Task MarkAsSeenAsync(
|
||||||
EmailAccountDto account,
|
EmailAccountDto account,
|
||||||
long uid,
|
long uid,
|
||||||
|
|||||||
Reference in New Issue
Block a user