summaryrefslogtreecommitdiffstats
path: root/content/modules/core.js
diff options
context:
space:
mode:
Diffstat (limited to 'content/modules/core.js')
-rw-r--r--content/modules/core.js332
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);
+ }
+
+}