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 : ICalendarServiceEx { 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); // 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> GetMailCopiesAsync(IEnumerable mailCopyIds); 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); Task> AreMailsExistsAsync(IEnumerable mailCopyIds); Task UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string synchronizationDeltaIdentifier); } 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, ICalendarServiceEx calendarServiceEx, IMimeFileService mimeFileService) : BaseDatabaseService(databaseService), IDefaultChangeProcessor, ICalendarServiceEx { protected IMailService MailService = mailService; protected ICalendarService CalendarService = calendarService; protected IFolderService FolderService = folderService; protected IAccountService AccountService = accountService; private readonly ICalendarServiceEx _calendarServiceEx = calendarServiceEx; private readonly IMimeFileService _mimeFileService = mimeFileService; public Task UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string synchronizationDeltaIdentifier) => AccountService.UpdateSyncIdentifierRawAsync(accountId, synchronizationDeltaIdentifier); public Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged) => MailService.ChangeFlagStatusAsync(mailCopyId, isFlagged); public Task> AreMailsExistsAsync(IEnumerable mailCopyIds) => MailService.AreMailsExistsAsync(mailCopyIds); public Task> GetMailCopiesAsync(IEnumerable mailCopyIds) => MailService.GetMailItemsAsync(mailCopyIds); 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); // TODO: Normalize this shit. Not everything needs to be exposed for processor. #region ICalendarServiceEx public Task ClearAllCalendarEventAttendeesAsync() { return _calendarServiceEx.ClearAllCalendarEventAttendeesAsync(); } public Task ClearAllCalendarsAsync() { return _calendarServiceEx.ClearAllCalendarsAsync(); } public Task ClearAllDataAsync() { return _calendarServiceEx.ClearAllDataAsync(); } public Task ClearAllEventsAsync() { return _calendarServiceEx.ClearAllEventsAsync(); } public Task DeleteCalendarAsync(string remoteCalendarId) { return _calendarServiceEx.DeleteCalendarAsync(remoteCalendarId); } public Task DeleteCalendarEventAttendeesForEventAsync(Guid eventId) { return _calendarServiceEx.DeleteCalendarEventAttendeesForEventAsync(eventId); } public Task DeleteEventAsync(string remoteEventId) { return _calendarServiceEx.DeleteEventAsync(remoteEventId); } public Task> GetAllCalendarEventAttendeesAsync() { return _calendarServiceEx.GetAllCalendarEventAttendeesAsync(); } public Task> GetAllCalendarsAsync() { return _calendarServiceEx.GetAllCalendarsAsync(); } public Task> GetAllDayEventsAsync() { return _calendarServiceEx.GetAllDayEventsAsync(); } public Task> GetAllEventsAsync() { return _calendarServiceEx.GetAllEventsAsync(); } public Task> GetAllEventsIncludingDeletedAsync() { return _calendarServiceEx.GetAllEventsIncludingDeletedAsync(); } public Task> GetAllRecurringEventsByTypeAsync() { return _calendarServiceEx.GetAllRecurringEventsByTypeAsync(); } public Task GetCalendarByRemoteIdAsync(string remoteCalendarId) { return _calendarServiceEx.GetCalendarByRemoteIdAsync(remoteCalendarId); } public Task> GetCalendarEventAttendeeResponseCountsAsync(Guid eventId) { return _calendarServiceEx.GetCalendarEventAttendeeResponseCountsAsync(eventId); } public Task> GetCalendarEventAttendeesForEventAsync(Guid eventId) { return _calendarServiceEx.GetCalendarEventAttendeesForEventAsync(eventId); } public Task> GetCalendarEventAttendeesForEventByRemoteIdAsync(string remoteEventId) { return _calendarServiceEx.GetCalendarEventAttendeesForEventByRemoteIdAsync(remoteEventId); } public Task GetCalendarSyncTokenAsync(string calendarId) { return _calendarServiceEx.GetCalendarSyncTokenAsync(calendarId); } public Task GetEventByRemoteIdAsync(string remoteEventId) { return _calendarServiceEx.GetEventByRemoteIdAsync(remoteEventId); } public Task> GetEventsByItemTypeAsync(CalendarItemType itemType) { return _calendarServiceEx.GetEventsByItemTypeAsync(itemType); } public Task> GetEventsByItemTypesAsync(params CalendarItemType[] itemTypes) { return _calendarServiceEx.GetEventsByItemTypesAsync(itemTypes); } public Task> GetEventsByremoteCalendarIdAsync(string remoteCalendarId) { return _calendarServiceEx.GetEventsByremoteCalendarIdAsync(remoteCalendarId); } public Task> GetEventsForCalendarAsync(Guid calendarId) { return _calendarServiceEx.GetEventsForCalendarAsync(calendarId); } public Task> GetEventsInDateRangeAsync(DateTime startDate, DateTime endDate) { return _calendarServiceEx.GetEventsInDateRangeAsync(startDate, endDate); } public Task> GetEventsSinceLastSyncAsync(DateTime? lastSyncTime) { return _calendarServiceEx.GetEventsSinceLastSyncAsync(lastSyncTime); } public Task> GetEventStatsByItemTypeAsync() { return _calendarServiceEx.GetEventStatsByItemTypeAsync(); } public Task> GetExpandedEventsInDateRangeAsync(DateTime startDate, DateTime endDate) { return _calendarServiceEx.GetExpandedEventsInDateRangeAsync(startDate, endDate); } public Task> GetExpandedEventsInDateRangeWithExceptionsAsync(DateTime startDate, DateTime endDate, AccountCalendar calendar) { return _calendarServiceEx.GetExpandedEventsInDateRangeWithExceptionsAsync(startDate, endDate, calendar); } public Task GetLastSyncTimeAsync(string calendarId) { return _calendarServiceEx.GetLastSyncTimeAsync(calendarId); } public Task> GetMultiDayEventsAsync() { return _calendarServiceEx.GetMultiDayEventsAsync(); } public Task> GetRecurringEventsAsync() { return _calendarServiceEx.GetRecurringEventsAsync(); } public Task HardDeleteEventAsync(string remoteEventId) { return _calendarServiceEx.HardDeleteEventAsync(remoteEventId); } public Task InsertCalendarAsync(AccountCalendar calendar) { return _calendarServiceEx.InsertCalendarAsync(calendar); } public Task InsertCalendarEventAttendeeAsync(CalendarEventAttendee calendareventattendee) { return _calendarServiceEx.InsertCalendarEventAttendeeAsync(calendareventattendee); } public Task InsertEventAsync(CalendarItem calendarItem) { return _calendarServiceEx.InsertEventAsync(calendarItem); } public Task MarkEventAsDeletedAsync(string remoteEventId, string remoteCalendarId) { return _calendarServiceEx.MarkEventAsDeletedAsync(remoteEventId, remoteCalendarId); } public Task SyncCalendarEventAttendeesForEventAsync(Guid eventId, List calendareventattendees) { return _calendarServiceEx.SyncCalendarEventAttendeesForEventAsync(eventId, calendareventattendees); } public Task UpdateAllEventItemTypesAsync() { return _calendarServiceEx.UpdateAllEventItemTypesAsync(); } public Task UpdateCalendarAsync(AccountCalendar calendar) { return _calendarServiceEx.UpdateCalendarAsync(calendar); } public Task UpdateCalendarEventAttendeeAsync(CalendarEventAttendee calendareventattendee) { return _calendarServiceEx.UpdateCalendarEventAttendeeAsync(calendareventattendee); } public Task UpdateCalendarSyncTokenAsync(string calendarId, string syncToken) { return _calendarServiceEx.UpdateCalendarSyncTokenAsync(calendarId, syncToken); } public Task UpdateEventAsync(CalendarItem calendarItem) { return _calendarServiceEx.UpdateEventAsync(calendarItem); } public Task UpsertCalendarAsync(AccountCalendar calendar) { return _calendarServiceEx.UpsertCalendarAsync(calendar); } public Task UpsertEventAsync(CalendarItem calendarItem) { return _calendarServiceEx.UpsertEventAsync(calendarItem); } public Task SyncAttendeesForEventAsync(Guid eventId, List attendees) { return _calendarServiceEx.SyncAttendeesForEventAsync(eventId, attendees); } #endregion }