using System; using System.Collections.Generic; using System.Threading.Tasks; using Google.Apis.Calendar.v3.Data; using MimeKit; using Wino.Core.Domain.Entities.Calendar; using Wino.Core.Domain.Entities.Mail; using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.MailItem; using Wino.Core.Domain.Models.Synchronization; using Wino.Services; namespace Wino.Core.Integration.Processors; /// /// Database change processor that handles common operations for all synchronizers. /// When a synchronizer detects a change, it should call the appropriate method in this class to reflect the change in the database. /// Different synchronizers might need additional implementations. /// , and /// None of the synchronizers can directly change anything in the database. /// public interface IDefaultChangeProcessor { Task UpdateAccountAsync(MailAccount account); Task UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string deltaSynchronizationIdentifier); Task DeleteAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId); Task ChangeMailReadStatusAsync(string mailCopyId, bool isRead); Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged); Task CreateMailAsync(Guid AccountId, NewMailItemPackage package); Task DeleteMailAsync(Guid accountId, string mailId); Task> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable downloadedMailCopyIds); Task SaveMimeFileAsync(Guid fileId, MimeMessage mimeMessage, Guid accountId); Task DeleteFolderAsync(Guid accountId, string remoteFolderId); Task InsertFolderAsync(MailItemFolder folder); Task UpdateFolderAsync(MailItemFolder folder); Task> GetLocalFoldersAsync(Guid accountId); Task> GetSynchronizationFoldersAsync(MailSynchronizationOptions options); Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId); Task UpdateFolderLastSyncDateAsync(Guid folderId); Task UpdateRemoteAliasInformationAsync(MailAccount account, List remoteAccountAliases); /// /// Interrupted initial synchronization may cause downloaded mails to be saved in the database twice. /// Since downloading mime is costly in Outlook, we need to check if the actual copy of the message has been saved before. /// This is also used in online search to prevent duplicate mails. /// /// MailCopyId of the message. /// Whether mail exists or not. Task IsMailExistsAsync(string messageId); // Calendar Task> GetAccountCalendarsAsync(Guid accountId); Task DeleteCalendarItemAsync(Guid calendarItemId); Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar); Task InsertAccountCalendarAsync(AccountCalendar accountCalendar); Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar); Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken); Task GetMailCopyAsync(string mailCopyId); Task CreateMailRawAsync(MailAccount account, MailItemFolder mailItemFolder, NewMailItemPackage package); Task DeleteUserMailCacheAsync(Guid accountId); /// /// Checks whether the mail exists in the folder. /// When deciding Create or Update existing mail, we need to check if the mail exists in the folder. /// Also duplicate assignments for Gmail's virtual Archive folder is ignored. /// /// Message id /// Folder's local id. /// Whether mail exists in the folder or not. Task IsMailExistsInFolderAsync(string messageId, Guid folderId); } public interface IGmailChangeProcessor : IDefaultChangeProcessor { Task HasAccountAnyDraftAsync(Guid accountId); Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId); Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId); Task ManageCalendarEventAsync(Event calendarEvent, AccountCalendar assignedCalendar, MailAccount organizerAccount); Task GetGmailArchiveComparisonResultAsync(Guid archiveFolderId, List onlineArchiveMailIds); } public interface IOutlookChangeProcessor : IDefaultChangeProcessor { /// /// Updates Folder's delta synchronization identifier. /// Only used in Outlook since it does per-folder sync. /// /// Folder id /// New synchronization identifier. /// New identifier if success. Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string deltaSynchronizationIdentifier); /// /// Outlook may expire folder's delta token after a while. /// Recommended action for this scenario is to reset token and do full sync. /// This method resets the token for the given folder. /// /// Local folder id to reset token for. /// Empty string to assign folder delta sync for. Task ResetFolderDeltaTokenAsync(Guid folderId); Task ManageCalendarEventAsync(Microsoft.Graph.Models.Event calendarEvent, AccountCalendar assignedCalendar, MailAccount organizerAccount); } public interface IImapChangeProcessor : IDefaultChangeProcessor { /// /// Returns all known uids for the given folder. /// /// Folder id to retrieve uIds for. Task> GetKnownUidsForFolderAsync(Guid folderId); } public class DefaultChangeProcessor(IDatabaseService databaseService, IFolderService folderService, IMailService mailService, ICalendarService calendarService, IAccountService accountService, IMimeFileService mimeFileService) : BaseDatabaseService(databaseService), IDefaultChangeProcessor { protected IMailService MailService = mailService; protected ICalendarService CalendarService = calendarService; protected IFolderService FolderService = folderService; protected IAccountService AccountService = accountService; private readonly IMimeFileService _mimeFileService = mimeFileService; public Task UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string synchronizationDeltaIdentifier) => AccountService.UpdateSynchronizationIdentifierAsync(accountId, synchronizationDeltaIdentifier); public Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged) => MailService.ChangeFlagStatusAsync(mailCopyId, isFlagged); public Task IsMailExistsAsync(string messageId) => MailService.IsMailExistsAsync(messageId); public Task GetMailCopyAsync(string mailCopyId) => MailService.GetSingleMailItemAsync(mailCopyId); public Task ChangeMailReadStatusAsync(string mailCopyId, bool isRead) => MailService.ChangeReadStatusAsync(mailCopyId, isRead); public Task DeleteAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId) => MailService.DeleteAssignmentAsync(accountId, mailCopyId, remoteFolderId); public Task DeleteMailAsync(Guid accountId, string mailId) => MailService.DeleteMailAsync(accountId, mailId); public Task CreateMailAsync(Guid accountId, NewMailItemPackage package) => MailService.CreateMailAsync(accountId, package); public Task CreateMailRawAsync(MailAccount account, MailItemFolder mailItemFolder, NewMailItemPackage package) => MailService.CreateMailRawAsync(account, mailItemFolder, package); public Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId) => MailService.MapLocalDraftAsync(accountId, localDraftCopyUniqueId, newMailCopyId, newDraftId, newThreadId); public Task> GetLocalFoldersAsync(Guid accountId) => FolderService.GetFoldersAsync(accountId); public Task> GetSynchronizationFoldersAsync(MailSynchronizationOptions options) => FolderService.GetSynchronizationFoldersAsync(options); public Task DeleteFolderAsync(Guid accountId, string remoteFolderId) => FolderService.DeleteFolderAsync(accountId, remoteFolderId); public Task InsertFolderAsync(MailItemFolder folder) => FolderService.InsertFolderAsync(folder); public Task UpdateFolderAsync(MailItemFolder folder) => FolderService.UpdateFolderAsync(folder); public Task> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable downloadedMailCopyIds) => MailService.GetDownloadedUnreadMailsAsync(accountId, downloadedMailCopyIds); public Task SaveMimeFileAsync(Guid fileId, MimeMessage mimeMessage, Guid accountId) => _mimeFileService.SaveMimeMessageAsync(fileId, mimeMessage, accountId); public Task UpdateFolderLastSyncDateAsync(Guid folderId) => FolderService.UpdateFolderLastSyncDateAsync(folderId); public Task UpdateAccountAsync(MailAccount account) => AccountService.UpdateAccountAsync(account); public Task UpdateRemoteAliasInformationAsync(MailAccount account, List remoteAccountAliases) => AccountService.UpdateRemoteAliasInformationAsync(account, remoteAccountAliases); public Task> GetAccountCalendarsAsync(Guid accountId) => CalendarService.GetAccountCalendarsAsync(accountId); public Task DeleteCalendarItemAsync(Guid calendarItemId) => CalendarService.DeleteCalendarItemAsync(calendarItemId); public Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar) => CalendarService.DeleteAccountCalendarAsync(accountCalendar); public Task InsertAccountCalendarAsync(AccountCalendar accountCalendar) => CalendarService.InsertAccountCalendarAsync(accountCalendar); public Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar) => CalendarService.UpdateAccountCalendarAsync(accountCalendar); public Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken) => CalendarService.UpdateCalendarDeltaSynchronizationToken(calendarId, deltaToken); public async Task DeleteUserMailCacheAsync(Guid accountId) { await _mimeFileService.DeleteUserMimeCacheAsync(accountId).ConfigureAwait(false); await AccountService.DeleteAccountMailCacheAsync(accountId, AccountCacheResetReason.ExpiredCache).ConfigureAwait(false); } public Task IsMailExistsInFolderAsync(string messageId, Guid folderId) => MailService.IsMailExistsAsync(messageId, folderId); }