/s
Simplify IMAP email fetching API and refactor logic Removed `markAsSeen` parameter from `FetchEmailsAsync` and `FetchEmailByUidAsync` methods in `IImapEmailService` to simplify the API. Updated `MailSearchFilter` to make `MaxCount` nullable for greater flexibility. Removed `markAsSeen` from `FetchEmailByUidQuery` and `FetchEmailsQuery` records and their handlers. Deleted `FetchEmailByUidQueryValidator` as it is no longer needed. Refactored `LimilabsImapEmailService`: - Introduced `FetchEmailUidsAsync` to centralize UID fetching logic. - Simplified `FetchEmailByUidAsync` using a new helper method. - Consolidated connection, authentication, and folder selection into reusable private methods. - Removed redundant code for search criteria and flag fetching. Removed `IsSeen` from `ReceivedEmailContext` and improved overall code readability and maintainability by reducing duplication and centralizing logic.
This commit is contained in:
@@ -13,7 +13,6 @@ public interface IImapEmailService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="account">Account whose IMAP settings will be used.</param>
|
/// <param name="account">Account whose IMAP settings will be used.</param>
|
||||||
/// <param name="filter">Filter to apply when fetching emails.</param>
|
/// <param name="filter">Filter to apply when fetching emails.</param>
|
||||||
/// <param name="markAsSeen">
|
|
||||||
/// When <see langword="true"/> (default), fetched messages are marked as <c>\Seen</c> on the server.
|
/// When <see langword="true"/> (default), fetched messages are marked as <c>\Seen</c> on the server.
|
||||||
/// Set to <see langword="false"/> for a non-destructive read (uses <c>BODY.PEEK</c> internally).
|
/// Set to <see langword="false"/> for a non-destructive read (uses <c>BODY.PEEK</c> internally).
|
||||||
/// </param>
|
/// </param>
|
||||||
@@ -21,7 +20,6 @@ public interface IImapEmailService
|
|||||||
Task<IEnumerable<ReceivedEmailContext>> FetchEmailsAsync(
|
Task<IEnumerable<ReceivedEmailContext>> FetchEmailsAsync(
|
||||||
EmailAccountDto account,
|
EmailAccountDto account,
|
||||||
MailSearchFilter filter,
|
MailSearchFilter filter,
|
||||||
bool markAsSeen = true,
|
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -35,7 +33,6 @@ public interface IImapEmailService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetches a single email by its UID.
|
/// Fetches a single email by its UID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="markAsSeen">
|
|
||||||
/// When <see langword="true"/> (default), the message is marked as <c>\Seen</c> on the server.
|
/// When <see langword="true"/> (default), the message is marked as <c>\Seen</c> on the server.
|
||||||
/// Set to <see langword="false"/> for a non-destructive read.
|
/// Set to <see langword="false"/> for a non-destructive read.
|
||||||
/// </param>
|
/// </param>
|
||||||
@@ -44,7 +41,6 @@ public interface IImapEmailService
|
|||||||
long uid,
|
long uid,
|
||||||
string folder = "INBOX",
|
string folder = "INBOX",
|
||||||
bool withAttachments = false,
|
bool withAttachments = false,
|
||||||
bool markAsSeen = true,
|
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public record MailSearchFilter
|
|||||||
/// Maximum number of messages to retrieve. <c>0</c> means unlimited.
|
/// Maximum number of messages to retrieve. <c>0</c> means unlimited.
|
||||||
/// Applied after sorting; defaults to <c>50</c>.
|
/// Applied after sorting; defaults to <c>50</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxCount { get; init; } = 50;
|
public int? MaxCount { get; init; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Controls the order of the returned messages. Defaults to <see cref="MailSortOrder.NewestFirst"/>.
|
/// Controls the order of the returned messages. Defaults to <see cref="MailSortOrder.NewestFirst"/>.
|
||||||
|
|||||||
@@ -36,11 +36,6 @@ public record FetchEmailByUidQuery : IRequest<ReceivedEmailContext?>
|
|||||||
/// When <see langword="true"/>, attachment data is included in the result.
|
/// When <see langword="true"/>, attachment data is included in the result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool WithAttachments { get; init; } = false;
|
public bool WithAttachments { get; init; } = false;
|
||||||
/// <summary>
|
|
||||||
/// When <see langword="true"/> (default), the message is marked as <c>\Seen</c> on the server.
|
|
||||||
/// Set to <see langword="false"/> for a non-destructive peek.
|
|
||||||
/// </summary>
|
|
||||||
public bool MarkAsSeen { get; init; } = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FetchEmailByUidQueryHandler(
|
public class FetchEmailByUidQueryHandler(
|
||||||
@@ -62,7 +57,6 @@ public class FetchEmailByUidQueryHandler(
|
|||||||
(long)request.Uid!,
|
(long)request.Uid!,
|
||||||
request.Folder,
|
request.Folder,
|
||||||
request.WithAttachments,
|
request.WithAttachments,
|
||||||
request.MarkAsSeen,
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,6 @@ public record FetchEmailsQuery : IRequest<IEnumerable<ReceivedEmailContext>>
|
|||||||
/// Mail query used to filter and limit the emails retrieved.
|
/// Mail query used to filter and limit the emails retrieved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MailSearchFilter Mail { get; init; } = new();
|
public MailSearchFilter Mail { get; init; } = new();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When <see langword="true"/> (default), fetched messages are marked as <c>\Seen</c> on the server.
|
|
||||||
/// Set to <see langword="false"/> for a non-destructive peek.
|
|
||||||
/// </summary>
|
|
||||||
public bool MarkAsSeen { get; init; } = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FetchEmailsQueryHandler(
|
public class FetchEmailsQueryHandler(
|
||||||
@@ -46,7 +40,6 @@ public class FetchEmailsQueryHandler(
|
|||||||
return await ImapService.FetchEmailsAsync(
|
return await ImapService.FetchEmailsAsync(
|
||||||
account,
|
account,
|
||||||
request.Mail,
|
request.Mail,
|
||||||
request.MarkAsSeen,
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
using DigitalData.MessagingService.Application.EmailReceiving.Queries;
|
|
||||||
using FluentValidation;
|
|
||||||
|
|
||||||
namespace DigitalData.MessagingService.Application.EmailReceiving.Validators;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Validates a <see cref="FetchEmailByUidQuery"/> before it is handled by <see cref="FetchEmailByUidQueryHandler"/>.
|
|
||||||
/// </summary>
|
|
||||||
public class FetchEmailByUidQueryValidator : AbstractValidator<FetchEmailByUidQuery>
|
|
||||||
{
|
|
||||||
public FetchEmailByUidQueryValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.Account)
|
|
||||||
.NotNull()
|
|
||||||
.WithMessage("Account query must not be null.");
|
|
||||||
|
|
||||||
RuleFor(x => x.Uid)
|
|
||||||
.NotNull()
|
|
||||||
.WithMessage("UID must be provided. Use WithUid() to set the UID before dispatching the query.")
|
|
||||||
.GreaterThan(0)
|
|
||||||
.WithMessage("UID must be greater than 0.");
|
|
||||||
|
|
||||||
RuleFor(x => x.Folder)
|
|
||||||
.NotEmpty()
|
|
||||||
.WithMessage("Folder must not be empty.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,74 +27,16 @@ public class LimilabsImapEmailService(
|
|||||||
public async Task<IEnumerable<ReceivedEmailContext>> FetchEmailsAsync(
|
public async Task<IEnumerable<ReceivedEmailContext>> FetchEmailsAsync(
|
||||||
EmailAccountDto account,
|
EmailAccountDto account,
|
||||||
MailSearchFilter filter,
|
MailSearchFilter filter,
|
||||||
bool markAsSeen = true,
|
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
using var imap = new Imap();
|
using var imap = new Imap();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ConnectAndAuthenticateAsync(imap, account);
|
var uids = await FetchEmailUidsAsync(imap, account, filter, cancellationToken);
|
||||||
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));
|
|
||||||
|
|
||||||
// IMAP BEFORE is exclusive, so add one day to make the bound inclusive
|
|
||||||
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)];
|
|
||||||
|
|
||||||
if (uids.Count == 0)
|
if (uids.Count == 0)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
// Fetch flags for all UIDs in one round-trip
|
|
||||||
var allFlags = await imap.GetFlagsByUIDAsync(uids, cancellationToken);
|
|
||||||
var flagsById = allFlags
|
|
||||||
.Where(f => f.UID.HasValue)
|
|
||||||
.ToDictionary(f => f.UID!.Value, f => f.Flags);
|
|
||||||
|
|
||||||
// markAsSeen=true → BODY[] (server sets \Seen automatically)
|
|
||||||
// markAsSeen=false → BODY.PEEK[] (\Seen untouched, non-destructive read)
|
|
||||||
var results = new List<ReceivedEmailContext>(uids.Count);
|
var results = new List<ReceivedEmailContext>(uids.Count);
|
||||||
|
|
||||||
foreach (var uid in uids)
|
foreach (var uid in uids)
|
||||||
@@ -103,12 +45,10 @@ public class LimilabsImapEmailService(
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var eml = markAsSeen
|
var email = await FetchEmailByUidAsync(imap, uid, filter.WithAttachments, cancellationToken)
|
||||||
? await imap.GetMessageByUIDAsync(uid, cancellationToken)
|
?? throw new NotFoundException($"Email with UID={uid} not found in folder '{filter.Folder}' in {account.Username}.");
|
||||||
: await imap.PeekMessageByUIDAsync(uid, cancellationToken);
|
|
||||||
var mail = new MailBuilder().CreateFromEml(eml);
|
results.Add(email);
|
||||||
flagsById.TryGetValue(uid, out var flags);
|
|
||||||
results.Add(MapToContext(uid, mail, flags, filter.WithAttachments));
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -146,52 +86,7 @@ public class LimilabsImapEmailService(
|
|||||||
await ConnectAndAuthenticateAsync(imap, account);
|
await ConnectAndAuthenticateAsync(imap, account);
|
||||||
await SelectFolderAsync(imap, filter.Folder);
|
await SelectFolderAsync(imap, filter.Folder);
|
||||||
|
|
||||||
List<ICriterion> criterions = [];
|
List<long> uids = await FetchEmailUidsAsync(imap, account, filter, cancellationToken);
|
||||||
|
|
||||||
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);
|
await imap.CloseAsync(cancellationToken);
|
||||||
return uids;
|
return uids;
|
||||||
@@ -215,7 +110,6 @@ public class LimilabsImapEmailService(
|
|||||||
long uid,
|
long uid,
|
||||||
string folder = "INBOX",
|
string folder = "INBOX",
|
||||||
bool withAttachments = false,
|
bool withAttachments = false,
|
||||||
bool markAsSeen = true,
|
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
using var imap = new Imap();
|
using var imap = new Imap();
|
||||||
@@ -224,28 +118,24 @@ public class LimilabsImapEmailService(
|
|||||||
await ConnectAndAuthenticateAsync(imap, account);
|
await ConnectAndAuthenticateAsync(imap, account);
|
||||||
await SelectFolderAsync(imap, folder);
|
await SelectFolderAsync(imap, folder);
|
||||||
|
|
||||||
var eml = markAsSeen
|
var mail = await FetchEmailByUidAsync(imap, uid, withAttachments, cancellationToken);
|
||||||
? await imap.GetMessageByUIDAsync(uid, cancellationToken)
|
|
||||||
: await imap.PeekMessageByUIDAsync(uid, cancellationToken);
|
|
||||||
var mail = new MailBuilder().CreateFromEml(eml);
|
|
||||||
var flags = await imap.GetFlagsByUIDAsync(uid, cancellationToken);
|
|
||||||
|
|
||||||
await imap.CloseAsync(cancellationToken);
|
await imap.CloseAsync(cancellationToken);
|
||||||
return MapToContext(uid, mail, flags, withAttachments);
|
return mail;
|
||||||
}
|
}
|
||||||
catch (Limilabs.Client.ServerException ex)
|
catch (Limilabs.Client.ServerException ex)
|
||||||
{
|
{
|
||||||
await imap.CloseSafelyAsync();
|
await imap.CloseSafelyAsync();
|
||||||
throw new AuthenticationFailedException(
|
throw new AuthenticationFailedException(
|
||||||
$"IMAP authentication failed for account '{account.Username}'.", ex);
|
$"IMAP authentication failed for account '{account.Username}'.", ex);
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||||
{
|
{
|
||||||
await imap.CloseSafelyAsync();
|
await imap.CloseSafelyAsync();
|
||||||
logger.LogWarning(ex, "Failed to fetch IMAP message UID={Uid} from folder {Folder}.", uid, folder);
|
logger.LogWarning(ex, "Failed to fetch IMAP message UID={Uid} from folder {Folder}.", uid, folder);
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public async Task MarkAsSeenAsync(
|
public async Task MarkAsSeenAsync(
|
||||||
EmailAccountDto account,
|
EmailAccountDto account,
|
||||||
@@ -276,6 +166,118 @@ public class LimilabsImapEmailService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Private helpers
|
// Private helpers
|
||||||
|
private async Task<List<long>> FetchEmailUidsAsync(
|
||||||
|
Imap imap,
|
||||||
|
EmailAccountDto account,
|
||||||
|
MailSearchFilter filter,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
|
||||||
|
// IMAP BEFORE is exclusive, so add one day to make the bound inclusive
|
||||||
|
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 is int maxCount && maxCount > 0 && uids.Count > maxCount)
|
||||||
|
uids = [.. uids.Take(maxCount)];
|
||||||
|
|
||||||
|
return uids;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ReceivedEmailContext?> FetchEmailByUidAsync(
|
||||||
|
Imap imap,
|
||||||
|
long uid,
|
||||||
|
bool withAttachments = false,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var eml = await imap.GetMessageByUIDAsync(uid, cancellationToken);
|
||||||
|
var mail = new MailBuilder().CreateFromEml(eml);
|
||||||
|
|
||||||
|
var attachments = new List<EmailAttachmentContext>();
|
||||||
|
|
||||||
|
if (withAttachments)
|
||||||
|
{
|
||||||
|
foreach (var att in mail.Attachments)
|
||||||
|
{
|
||||||
|
attachments.Add(new EmailAttachmentContext
|
||||||
|
{
|
||||||
|
FileName = att.FileName ?? "attachment",
|
||||||
|
Content = att.Data,
|
||||||
|
ContentType = att.ContentType?.ToString() ?? "application/octet-stream",
|
||||||
|
IsInline = false,
|
||||||
|
ContentId = att.ContentId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var vis in mail.Visuals)
|
||||||
|
{
|
||||||
|
attachments.Add(new EmailAttachmentContext
|
||||||
|
{
|
||||||
|
FileName = vis.FileName ?? "inline",
|
||||||
|
Content = vis.Data,
|
||||||
|
ContentType = vis.ContentType?.ToString() ?? "application/octet-stream",
|
||||||
|
IsInline = true,
|
||||||
|
ContentId = vis.ContentId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ReceivedEmailContext
|
||||||
|
{
|
||||||
|
Uid = uid,
|
||||||
|
From = mail.From.FirstOrDefault()?.Address ?? string.Empty,
|
||||||
|
To = [.. mail.To.SelectMany(m => m.GetMailboxes()).Select(mb => mb.Address)],
|
||||||
|
Cc = [.. mail.Cc.SelectMany(m => m.GetMailboxes()).Select(mb => mb.Address)],
|
||||||
|
Subject = mail.Subject ?? string.Empty,
|
||||||
|
TextBody = mail.Text ?? string.Empty,
|
||||||
|
HtmlBody = mail.Html ?? string.Empty,
|
||||||
|
Date = mail.Date ?? DateTime.MinValue,
|
||||||
|
Attachments = attachments,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ConnectAndAuthenticateAsync(Imap imap, EmailAccountDto account)
|
private async Task ConnectAndAuthenticateAsync(Imap imap, EmailAccountDto account)
|
||||||
{
|
{
|
||||||
if (account.ImapUseSsl)
|
if (account.ImapUseSsl)
|
||||||
@@ -297,50 +299,4 @@ public class LimilabsImapEmailService(
|
|||||||
else
|
else
|
||||||
await imap.SelectAsync(folder);
|
await imap.SelectAsync(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReceivedEmailContext MapToContext(long uid, IMail mail, List<Flag>? flags, bool withAttachments = false)
|
|
||||||
{
|
|
||||||
var attachments = new List<EmailAttachmentContext>();
|
|
||||||
|
|
||||||
if (withAttachments)
|
|
||||||
{
|
|
||||||
foreach (var att in mail.Attachments)
|
|
||||||
{
|
|
||||||
attachments.Add(new EmailAttachmentContext
|
|
||||||
{
|
|
||||||
FileName = att.FileName ?? "attachment",
|
|
||||||
Content = att.Data,
|
|
||||||
ContentType = att.ContentType?.ToString() ?? "application/octet-stream",
|
|
||||||
IsInline = false,
|
|
||||||
ContentId = att.ContentId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var vis in mail.Visuals)
|
|
||||||
{
|
|
||||||
attachments.Add(new EmailAttachmentContext
|
|
||||||
{
|
|
||||||
FileName = vis.FileName ?? "inline",
|
|
||||||
Content = vis.Data,
|
|
||||||
ContentType = vis.ContentType?.ToString() ?? "application/octet-stream",
|
|
||||||
IsInline = true,
|
|
||||||
ContentId = vis.ContentId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ReceivedEmailContext
|
|
||||||
{
|
|
||||||
Uid = uid,
|
|
||||||
From = mail.From.FirstOrDefault()?.Address ?? string.Empty,
|
|
||||||
To = [.. mail.To.SelectMany(m => m.GetMailboxes()).Select(mb => mb.Address)],
|
|
||||||
Cc = [.. mail.Cc.SelectMany(m => m.GetMailboxes()).Select(mb => mb.Address)],
|
|
||||||
Subject = mail.Subject ?? string.Empty,
|
|
||||||
TextBody = mail.Text ?? string.Empty,
|
|
||||||
HtmlBody = mail.Html ?? string.Empty,
|
|
||||||
Date = mail.Date ?? DateTime.MinValue,
|
|
||||||
Attachments = attachments,
|
|
||||||
IsSeen = flags?.Contains(Flag.Seen) ?? false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user