diff options
Diffstat (limited to 'content/modules/core.js')
-rw-r--r-- | content/modules/core.js | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/content/modules/core.js b/content/modules/core.js new file mode 100644 index 0000000..6a82af3 --- /dev/null +++ b/content/modules/core.js @@ -0,0 +1,332 @@ +/* + * This file is part of TbSync. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + + "use strict"; + +var core = { + + syncDataObj : null, + + load: async function () { + this.syncDataObj = {}; + }, + + unload: async function () { + }, + + isSyncing: function (accountID) { + let status = TbSync.db.getAccountProperty(accountID, "status"); //global status of the account + return (status == "syncing"); + }, + + isEnabled: function (accountID) { + let status = TbSync.db.getAccountProperty(accountID, "status"); + return (status != "disabled"); + }, + + isConnected: function (accountID) { + let status = TbSync.db.getAccountProperty(accountID, "status"); + let validFolders = TbSync.db.findFolders({"cached": false}, {"accountID": accountID}); + return (status != "disabled" && validFolders.length > 0); + }, + + resetSyncDataObj: function (accountID) { + this.syncDataObj[accountID] = new TbSync.SyncData(accountID); + }, + + getSyncDataObject: function (accountID) { + if (!this.syncDataObj.hasOwnProperty(accountID)) { + this.resetSyncDataObj(accountID); + } + return this.syncDataObj[accountID]; + }, + + getNextPendingFolder: function (syncData) { + let sortedFolders = TbSync.providers[syncData.accountData.getAccountProperty("provider")].Base.getSortedFolders(syncData.accountData); + for (let i=0; i < sortedFolders.length; i++) { + if (sortedFolders[i].getFolderProperty("status") != "pending") continue; + syncData._setCurrentFolderData(sortedFolders[i]); + return true; + } + syncData._clearCurrentFolderData(); + return false; + }, + + + syncAllAccounts: function () { + //get info of all accounts + let accounts = TbSync.db.getAccounts(); + + for (let i=0; i < accounts.IDs.length; i++) { + // core async sync function, but we do not wait until it has finished, + // but return right away and initiate sync of all accounts parallel + this.syncAccount(accounts.IDs[i]); + } + }, + + syncAccount: async function (accountID, aSyncDescription = {}) { + let syncDescription = {}; + Object.assign(syncDescription, aSyncDescription); + + if (!syncDescription.hasOwnProperty("maxAccountReruns")) syncDescription.maxAccountReruns = 2; + if (!syncDescription.hasOwnProperty("maxFolderReruns")) syncDescription.maxFolderReruns = 2; + if (!syncDescription.hasOwnProperty("syncList")) syncDescription.syncList = true; + if (!syncDescription.hasOwnProperty("syncFolders")) syncDescription.syncFolders = null; // null ( = default = sync selected folders) or (empty) Array with folderData obj to be synced + if (!syncDescription.hasOwnProperty("syncJob")) syncDescription.syncJob = "sync"; + + //do not init sync if there is a sync running or account is not enabled + if (!this.isEnabled(accountID) || this.isSyncing(accountID)) return; + + //create syncData object for each account (to be able to have parallel XHR) + this.resetSyncDataObj(accountID); + let syncData = this.getSyncDataObject(accountID); + + //send GUI into lock mode (status == syncing) + TbSync.db.setAccountProperty(accountID, "status", "syncing"); + Services.obs.notifyObservers(null, "tbsync.observer.manager.updateAccountSettingsGui", accountID); + + let overallStatusData = new TbSync.StatusData(); + let accountRerun; + let accountRuns = 0; + + do { + accountRerun = false; + + if (accountRuns > syncDescription.maxAccountReruns) { + overallStatusData = new TbSync.StatusData(TbSync.StatusData.ERROR, "resync-loop"); + break; + } + accountRuns++; + + if (syncDescription.syncList) { + let listStatusData; + try { + listStatusData = await TbSync.providers[syncData.accountData.getAccountProperty("provider")].Base.syncFolderList(syncData, syncDescription.syncJob, accountRuns); + } catch (e) { + listStatusData = new TbSync.StatusData(TbSync.StatusData.WARNING, "JavaScriptError", e.message + "\n\n" + e.stack); + } + + if (!(listStatusData instanceof TbSync.StatusData)) { + overallStatusData = new TbSync.StatusData(TbSync.StatusData.ERROR, "apiError", "TbSync/"+syncData.accountData.getAccountProperty("provider")+": Base.syncFolderList() must return a StatusData object"); + break; + } + + //if we have an error during folderList sync, there is no need to go on + if (listStatusData.type != TbSync.StatusData.SUCCESS) { + overallStatusData = listStatusData; + accountRerun = (listStatusData.type == TbSync.StatusData.ACCOUNT_RERUN) + TbSync.eventlog.add(listStatusData.type, syncData.eventLogInfo, listStatusData.message, listStatusData.details); + continue; //jumps to the while condition check + } + + // Removes all leftover cached folders and sets all other folders to a well defined cached = "0" + // which will set this account as connected (if at least one non-cached folder is present). + this.removeCachedFolders(syncData); + + // update folder list in GUI + Services.obs.notifyObservers(null, "tbsync.observer.manager.updateFolderList", syncData.accountData.accountID); + } + + // syncDescription.syncFolders is either null ( = default = sync selected folders) or an Array. + // Skip folder sync if Array is empty. + if (!Array.isArray(syncDescription.syncFolders) || syncDescription.syncFolders.length > 0) { + this.prepareFoldersForSync(syncData, syncDescription); + + // update folder list in GUI + Services.obs.notifyObservers(null, "tbsync.observer.manager.updateFolderList", syncData.accountData.accountID); + + // if any folder was found, sync + if (syncData.accountData.isConnected()) { + let folderRuns = 1; + do { + if (folderRuns > syncDescription.maxFolderReruns) { + overallStatusData = new TbSync.StatusData(TbSync.StatusData.ERROR, "resync-loop"); + break; + } + + // getNextPendingFolder will set or clear currentFolderData of syncData + if (!this.getNextPendingFolder(syncData)) { + break; + } + + let folderStatusData; + try { + folderStatusData = await TbSync.providers[syncData.accountData.getAccountProperty("provider")].Base.syncFolder(syncData, syncDescription.syncJob, folderRuns); + } catch (e) { + folderStatusData = new TbSync.StatusData(TbSync.StatusData.WARNING, "JavaScriptError", e.message + "\n\n" + e.stack); + } + + if (!(folderStatusData instanceof TbSync.StatusData)) { + folderStatusData = new TbSync.StatusData(TbSync.StatusData.ERROR, "apiError", "TbSync/"+syncData.accountData.getAccountProperty("provider")+": Base.syncFolder() must return a StatusData object"); + } + + // if one of the folders indicated a FOLDER_RERUN, do not finish this + // folder but do it again + if (folderStatusData.type == TbSync.StatusData.FOLDER_RERUN) { + TbSync.eventlog.add(folderStatusData.type, syncData.eventLogInfo, folderStatusData.message, folderStatusData.details); + folderRuns++; + continue; + } else { + folderRuns = 1; + } + + this.finishFolderSync(syncData, folderStatusData); + + //if one of the folders indicated an ERROR, abort sync + if (folderStatusData.type == TbSync.StatusData.ERROR) { + break; + } + + //if the folder has send an ACCOUNT_RERUN, abort sync and rerun the entire account + if (folderStatusData.type == TbSync.StatusData.ACCOUNT_RERUN) { + syncDescription.syncList = true; + accountRerun = true; + break; + } + + } while (true); + } else { + overallStatusData = new TbSync.StatusData(TbSync.StatusData.ERROR, "no-folders-found-on-server"); + } + } + + } while (accountRerun); + + this.finishAccountSync(syncData, overallStatusData); + }, + + // this could be added to AccountData, but I do not want that in public + setTargetModified: function (folderData) { + if (!folderData.accountData.isSyncing() && folderData.accountData.isEnabled()) { + folderData.accountData.setAccountProperty("status", "notsyncronized"); + folderData.setFolderProperty("status", "modified"); + //notify settings gui to update status + Services.obs.notifyObservers(null, "tbsync.observer.manager.updateSyncstate", folderData.accountID); + } + }, + + enableAccount: function(accountID) { + let accountData = new TbSync.AccountData(accountID); + TbSync.providers[accountData.getAccountProperty("provider")].Base.onEnableAccount(accountData); + accountData.setAccountProperty("status", "notsyncronized"); + accountData.resetAccountProperty("lastsynctime"); + }, + + disableAccount: function(accountID) { + let accountData = new TbSync.AccountData(accountID); + TbSync.providers[accountData.getAccountProperty("provider")].Base.onDisableAccount(accountData); + accountData.setAccountProperty("status", "disabled"); + + let folders = accountData.getAllFolders(); + for (let folder of folders) { + if (folder.getFolderProperty("selected")) { + folder.targetData.removeTarget(); + folder.setFolderProperty("selected", false); + } + folder.setFolderProperty("cached", true); + } + }, + + //removes all leftover cached folders and sets all other folders to a well defined cached = "0" + //which will set this account as connected (if at least one non-cached folder is present) + removeCachedFolders: function(syncData) { + let folders = syncData.accountData.getAllFoldersIncludingCache(); + for (let folder of folders) { + //delete all leftover cached folders + if (folder.getFolderProperty("cached")) { + TbSync.db.deleteFolder(folder.accountID, folder.folderID); + continue; + } else { + //set well defined cache state + folder.setFolderProperty("cached", false); + } + } + }, + + //set allrequested folders to "pending", so they are marked for syncing + prepareFoldersForSync: function(syncData, syncDescription) { + let folders = syncData.accountData.getAllFolders(); + for (let folder of folders) { + let requested = (Array.isArray(syncDescription.syncFolders) && syncDescription.syncFolders.filter(f => f.folderID == folder.folderID).length > 0); + let selected = (!Array.isArray(syncDescription.syncFolders) && folder.getFolderProperty("selected")); + + //set folders to pending, so they get synced + if (requested || selected) { + folder.setFolderProperty("status", "pending"); + } + } + }, + + finishFolderSync: function(syncData, statusData) { + if (statusData.type != TbSync.StatusData.SUCCESS) { + //report error + TbSync.eventlog.add(statusData.type, syncData.eventLogInfo, statusData.message, statusData.details); + } + + //if this is a success, prepend success to the status message, + //otherwise just set the message + let status; + if (statusData.type == TbSync.StatusData.SUCCESS || statusData.message == "") { + status = statusData.type; + if (statusData.message) status = status + "." + statusData.message; + } else { + status = statusData.message; + } + + if (syncData.currentFolderData) { + syncData.currentFolderData.setFolderProperty("status", status); + syncData.currentFolderData.setFolderProperty("lastsynctime", Date.now()); + //clear folderID to fall back to account-only-mode (folder is done!) + syncData._clearCurrentFolderData(); + } + + syncData.setSyncState("done"); + }, + + finishAccountSync: function(syncData, statusData) { + // set each folder with PENDING status to ABORTED + let folders = TbSync.db.findFolders({"status": "pending"}, {"accountID": syncData.accountData.accountID}); + for (let i=0; i < folders.length; i++) { + TbSync.db.setFolderProperty(folders[i].accountID, folders[i].folderID, "status", "aborted"); + } + + //if this is a success, prepend success to the status message, + //otherwise just set the message + let status; + if (statusData.type == TbSync.StatusData.SUCCESS || statusData.message == "") { + status = statusData.type; + if (statusData.message) status = status + "." + statusData.message; + } else { + status = statusData.message; + } + + + if (statusData.type != TbSync.StatusData.SUCCESS) { + //report error + TbSync.eventlog.add("warning", syncData.eventLogInfo, statusData.message, statusData.details); + } else { + //account itself is ok, search for folders with error + folders = TbSync.db.findFolders({"selected": true, "cached": false}, {"accountID": syncData.accountData.accountID}); + for (let i in folders) { + let folderstatus = folders[i].data.status.split(".")[0]; + if (folderstatus != "" && folderstatus != TbSync.StatusData.SUCCESS && folderstatus != "aborted") { + status = "foldererror"; + break; + } + } + } + + //done + syncData.accountData.setAccountProperty("lastsynctime", Date.now()); + syncData.accountData.setAccountProperty("status", status); + syncData.setSyncState("accountdone"); + Services.obs.notifyObservers(null, "tbsync.observer.manager.updateFolderList", syncData.accountData.accountID); + this.resetSyncDataObj(syncData.accountData.accountID); + } + +} |