using Bit.Core.Abstractions; using Bit.Core.Models.Data; using Bit.Core.Models.Response; using Bit.Core.Utilities; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Bit.Core.Services { public class SyncService { private const string Keys_LastSyncFormat = "lastSync_{0}"; private readonly IUserService _userService; private readonly IApiService _apiService; private readonly ISettingsService _settingsService; private readonly IFolderService _folderService; private readonly ICipherService _cipherService; private readonly ICryptoService _cryptoService; private readonly ICollectionService _collectionService; private readonly IStorageService _storageService; private readonly IMessagingService _messagingService; public SyncService( IUserService userService, IApiService apiService, ISettingsService settingsService, IFolderService folderService, ICipherService cipherService, ICryptoService cryptoService, ICollectionService collectionService, IStorageService storageService, IMessagingService messagingService) { _userService = userService; _apiService = apiService; _settingsService = settingsService; _folderService = folderService; _cipherService = cipherService; _cryptoService = cryptoService; _collectionService = collectionService; _storageService = storageService; _messagingService = messagingService; } public bool SyncInProgress { get; set; } public async Task GetLastSyncAsync() { var userId = await _userService.GetUserIdAsync(); if(userId == null) { return null; } return await _storageService.GetAsync(string.Format(Keys_LastSyncFormat, userId)); } public async Task SetLastSync(DateTime date) { var userId = await _userService.GetUserIdAsync(); if(userId == null) { return; } await _storageService.SaveAsync(string.Format(Keys_LastSyncFormat, userId), date); } // Helpers private void SyncStarted() { SyncInProgress = true; _messagingService.Send("syncStarted"); } private bool SyncCompelted(bool successfully) { SyncInProgress = false; _messagingService.Send("syncCompleted", new Dictionary { ["successfully"] = successfully }); return successfully; } private async Task> NeedsSyncingAsync(bool forceSync) { if(forceSync) { return new Tuple(true, false); } var lastSync = await GetLastSyncAsync(); if(lastSync == null || lastSync == DateTime.MinValue) { return new Tuple(true, false); } try { var response = await _apiService.GetAccountRevisionDateAsync(); var d = CoreHelpers.Epoc.AddMilliseconds(response); if(d <= lastSync.Value) { return new Tuple(false, false); } return new Tuple(true, false); } catch { return new Tuple(false, true); } } private async Task SyncProfileAsync(ProfileResponse response) { var stamp = await _userService.GetSecurityStampAsync(); if(stamp != null && stamp != response.SecurityStamp) { // TODO logout callback throw new Exception("Stamp has changed."); } await _cryptoService.SetEncKeyAsync(response.Key); await _cryptoService.SetEncPrivateKeyAsync(response.PrivateKey); await _cryptoService.SetOrgKeysAsync(response.Organizations); await _userService.SetSecurityStampAsync(response.SecurityStamp); var organizations = response.Organizations.ToDictionary(o => o.Id, o => new OrganizationData(o)); await _userService.ReplaceOrganizationsAsync(organizations); } private async Task SyncFoldersAsync(string userId, List response) { var folders = response.ToDictionary(f => f.Id, f => new FolderData(f, userId)); await _folderService.ReplaceAsync(folders); } private async Task SyncCollectionsAsync(List response) { var collections = response.ToDictionary(c => c.Id, c => new CollectionData(c)); await _collectionService.ReplaceAsync(collections); } private async Task SyncCiphersAsync(string userId, List response) { var ciphers = response.ToDictionary(c => c.Id, c => new CipherData(c, userId)); await _cipherService.ReplaceAsync(ciphers); } private async Task SyncSettingsAsync(string userId, DomainsResponse response) { var eqDomains = new List>(); if(response != null && response.EquivalentDomains != null) { eqDomains = eqDomains.Concat(response.EquivalentDomains).ToList(); } if(response != null && response.GlobalEquivalentDomains != null) { foreach(var global in response.GlobalEquivalentDomains) { if(global.Domains.Any()) { eqDomains.Add(global.Domains); } } } await _settingsService.SetEquivalentDomainsAsync(eqDomains); } } }