diff -Nru dav4tbsync-0.11/bootstrap.js dav4tbsync-0.15/bootstrap.js --- dav4tbsync-0.11/bootstrap.js 2019-01-31 18:54:04.000000000 +0000 +++ dav4tbsync-0.15/bootstrap.js 2019-02-26 16:26:01.000000000 +0000 @@ -15,11 +15,18 @@ let onInitDoneObserver = { observe: Task.async (function* (aSubject, aTopic, aData) { - //it is now safe to import tbsync.jsm - Components.utils.import("chrome://tbsync/content/tbsync.jsm"); + let valid = false; + try { + Components.utils.import("chrome://tbsync/content/tbsync.jsm"); + valid = tbSync.enabled; + } catch (e) { + //if this fails, tbSync is not loaded yet and we will get the notification later again + } - //load all providers of this provider add-on into TbSync (one at a time, obey order) - yield tbSync.loadProvider(thisID, "dav", "//dav4tbsync/content/dav.js"); + //load this provider add-on into TbSync + if (valid) { + yield tbSync.loadProvider(thisID, "dav", "//dav4tbsync/content/dav.js"); + } }) } @@ -45,21 +52,18 @@ //during app startup, the load of the provider will be triggered by a "tbsync.init.done" notification, //if load happens later, we need load manually if (reason != APP_STARTUP) { - //OLD API - REMOVE AFTER SWITCH - Services.obs.notifyObservers(null, "tbsync.addProvider", "dav"); onInitDoneObserver.observe(); } } function shutdown(data, reason) { - //OLD API - REMOVE AFTER SWITCH - Services.obs.notifyObservers(null, "tbsync.removeProvider", "dav"); - Services.obs.removeObserver(onInitDoneObserver, "tbsync.init.done"); //unload this provider add-on and all its loaded providers from TbSync try { tbSync.unloadProviderAddon(data.id); - } catch (e) {} + } catch (e) { + //if this fails, tbSync has been unloaded already but has unloaded this addon as well + } Services.obs.notifyObservers(null, "chrome-flush-caches", null); } diff -Nru dav4tbsync-0.11/content/dav.js dav4tbsync-0.15/content/dav.js --- dav4tbsync-0.11/content/dav.js 2019-01-31 18:54:04.000000000 +0000 +++ dav4tbsync-0.15/content/dav.js 2019-02-26 16:26:01.000000000 +0000 @@ -79,8 +79,16 @@ if (folders.length == 1) { switch (aName) { case "color": + //prepare connection data + let connection = {}; + connection.account = folders[0].account; + connection.folderID = folders[0].folderID; + connection.type = "cal"; + connection.fqdn = folders[0].fqdn; + dav.tools.addAccountDataToConnectionData(connection); + //update stored color to recover after disable - dav.tools.sendRequest(""+(aValue + "FFFFFFFF").slice(0,9)+"", folders[0].folderID, "PROPPATCH", {account: folders[0].account, fqdn: folders[0].fqdn}); + dav.tools.sendRequest(""+(aValue + "FFFFFFFF").slice(0,9)+"", folders[0].folderID, "PROPPATCH", connection); break; } } @@ -140,10 +148,14 @@ */ load: Task.async (function* (lightningIsAvail) { //load overlays or do other init stuff, use lightningIsAvail to init stuff if lightning is installed - yield tbSync.overlayManager.registerOverlay("chrome://messenger/content/addressbook/abEditCardDialog.xul", "chrome://dav4tbsync/content/overlays/abCardWindow.xul"); - yield tbSync.overlayManager.registerOverlay("chrome://messenger/content/addressbook/abNewCardDialog.xul", "chrome://dav4tbsync/content/overlays/abCardWindow.xul"); - yield tbSync.overlayManager.registerOverlay("chrome://messenger/content/addressbook/addressbook.xul", "chrome://dav4tbsync/content/overlays/addressbookoverlay.xul"); - + dav.overlayManager = new OverlayManager({verbose: 0}); + yield dav.overlayManager.registerOverlay("chrome://messenger/content/addressbook/abNewCardDialog.xul", "chrome://dav4tbsync/content/overlays/abNewCardWindow.xul"); + yield dav.overlayManager.registerOverlay("chrome://messenger/content/addressbook/abNewCardDialog.xul", "chrome://dav4tbsync/content/overlays/abCardWindow.xul"); + yield dav.overlayManager.registerOverlay("chrome://messenger/content/addressbook/abEditCardDialog.xul", "chrome://dav4tbsync/content/overlays/abCardWindow.xul"); + yield dav.overlayManager.registerOverlay("chrome://messenger/content/addressbook/addressbook.xul", "chrome://dav4tbsync/content/overlays/addressbookoverlay.xul"); + yield dav.overlayManager.registerOverlay("chrome://messenger/content/addressbook/addressbook.xul", "chrome://dav4tbsync/content/overlays/addressbookdetailsoverlay.xul"); + dav.overlayManager.startObserving(); + if (lightningIsAvail) { cal.getCalendarManager().addObserver(tbSync.dav.calendarManagerObserver); cal.getCalendarManager().addCalendarObserver(tbSync.dav.calendarObserver); @@ -187,7 +199,8 @@ if (lightningIsAvail) { cal.getCalendarManager().removeObserver(tbSync.dav.calendarManagerObserver); cal.getCalendarManager().removeCalendarObserver(tbSync.dav.calendarObserver); - } + } + dav.overlayManager.stopObserving(); }, @@ -231,7 +244,7 @@ if (accountId !== null) { let serviceprovider = tbSync.db.getAccountSetting(accountId, "serviceprovider"); if (tbSync.dav.serviceproviders.hasOwnProperty(serviceprovider)) { - base = tbSync.dav.serviceproviders[serviceprovider].icon; + base = tbSync.dav.serviceproviders[serviceprovider].icon; } } @@ -316,7 +329,6 @@ "createdWithProviderVersion" : "0", "syncGroups" : "0", - "useHomeAsPrimary" : "0", "useCache" : "1", "useCardBook" : "0", }; @@ -336,7 +348,6 @@ //so we need to store the fqdn information per folders "fqdn" : "", - "url" : "", "name" : "", "type" : "", //cladav, carddav or ics "shared": "", //identify shared resources @@ -438,7 +449,17 @@ //This example implementation is using the standard address book, but you may use another one let abManager = Components.classes["@mozilla.org/abmanager;1"].getService(Components.interfaces.nsIAbManager); - return abManager.newAddressBook(newname, "", 2); + let dirPrefId = abManager.newAddressBook(newname, "", 2); + let data = abManager.getDirectoryFromId(dirPrefId); + if (data instanceof Components.interfaces.nsIAbDirectory && data.dirPrefId == dirPrefId) { + let serviceprovider = tbSync.db.getAccountSetting(account, "serviceprovider"); + let icon = "custom"; + if (dav.serviceproviders.hasOwnProperty(serviceprovider)) { + icon = dav.serviceproviders[serviceprovider].icon; + } + data.setStringValue("tbSyncIcon", "dav" + icon); + } + return dirPrefId; //change this to data on next big change }, @@ -519,7 +540,7 @@ * * @param account [in] id of the account which should be searched * @param currentQuery [in] search query - * @param caller [in] "autocomplete" or "search" + * @param caller [in] "autocomplete" or "search" */ //abServerSearch: Task.async (function* (account, currentQuery, caller) { @@ -528,49 +549,6 @@ - /** - * Is called if one or more cards have been selected in the addressbook, to update - * field information in the card view pane - * - * OPTIONAL, do not implement, if this provider is not adding any fields to the - * address book - * - * @param window [in] window obj of address book - * @param card [in] selected card - */ - onAbResultsPaneSelectionChanged: function (window, card) { - let cvPhMain = window.document.getElementById("cvPhMain"); - if (cvPhMain) { - let cvPhMainValue = card.getProperty("X-DAV-MainPhone",""); - if (cvPhMainValue) { - cvPhMain.textContent = cvPhMain.getAttribute("labelprefix") + " " + cvPhMainValue; - cvPhMain.hidden = false; - window.document.getElementById("cvbPhone").collapsed = false; - window.document.getElementById("cvhPhone").collapsed = false; - } - } - }, - - - - /** - * Is called if a card is loaded in the edit dialog to show/hide elements - * besides those of class type "Container" - * - * OPTIONAL, do not implement, if this provider is not manipulating - * the edit/new dialog beyond toggeling the elements of - * class "Container" - * - * @param document [in] document obj of edit/new dialog - * @param isOwnProvider [in] true if the open card belongs to this provider - */ - onAbCardLoad: function (document, isOwnProvider) { - document.getElementById("WorkAddress2Container").hidden = isOwnProvider; - document.getElementById("abHomeTab").children[1].hidden = isOwnProvider; - }, - - - /** * Is called if TbSync needs to synchronize an account. * diff -Nru dav4tbsync-0.11/content/manager/createAccount.js dav4tbsync-0.15/content/manager/createAccount.js --- dav4tbsync-0.11/content/manager/createAccount.js 2019-01-31 18:54:04.000000000 +0000 +++ dav4tbsync-0.15/content/manager/createAccount.js 2019-02-26 16:26:01.000000000 +0000 @@ -8,6 +8,7 @@ "use strict"; +Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/Task.jsm"); Components.utils.import("chrome://tbsync/content/tbsync.jsm"); @@ -16,11 +17,6 @@ startTime: 0, maxTimeout: 30, - onClose: function () { - return !document.documentElement.getButton("finish").disabled; - }, - - addProviderEntry: function (icon, serviceprovider) { let name = tbSync.getLocalizedMessage("add.serverprofile."+serviceprovider, "dav"); let description = tbSync.getLocalizedMessage("add.serverprofile."+serviceprovider+".description", "dav"); @@ -76,11 +72,33 @@ this.serviceproviderlist.appendChild(this.addProviderEntry(tbSync.dav.serviceproviders[p].icon +"32.png", p)); } this.serviceproviderlist.selectedIndex = 0; + this.validating = false; }, + + clearValues: function () { + //clear fields + this.elementUser.value = ""; + this.elementPass.value = ""; + this.elementServer.value = ""; + this.elementCalDavServer.value = ""; + this.elementCardDavServer.value = ""; + let serviceprovider = this.serviceproviderlist.value; + if (serviceprovider == "discovery" || serviceprovider == "custom") { + this.elementName.value = ""; + } else { + this.elementName.value = tbSync.getLocalizedMessage("add.serverprofile."+serviceprovider, "dav"); + } + }, + + showFirstPage: function () { + document.getElementById("tbsync.newaccount.wizard").canAdvance = true; + this.validating = false; + }, + showSecondPage: function () { - document.documentElement.getButton("finish").disabled = true; - + tbSyncDavNewAccount.onUserTextInput(); + let serviceprovider = this.serviceproviderlist.value; //show/hide additional descriptions (if avail) let dFound = 0; @@ -103,14 +121,6 @@ let dLabel = document.getElementById("tbsync.newaccount.details.header"); dLabel.hidden = (dFound == 0); - //clear fields - this.elementName.value = ""; - this.elementUser = document.getElementById('tbsync.newaccount.user'); - this.elementPass = document.getElementById('tbsync.newaccount.password'); - this.elementServer.value = ""; - this.elementCalDavServer.value = ""; - this.elementCardDavServer.value = ""; - //always show the two server URLs, excpet for "discovery" serviceprovider if (serviceprovider == "discovery") { document.getElementById("tbsync.newaccount.caldavserver.row").hidden = true; @@ -130,11 +140,14 @@ document.getElementById("tbsync.newaccount.carddavserver.row").hidden = true; this.elementCalDavServer.disabled = true; this.elementCardDavServer.disabled = true; - this.elementName.value = tbSync.getLocalizedMessage("add.serverprofile."+serviceprovider, "dav"); this.elementCalDavServer.value = tbSync.dav.serviceproviders[serviceprovider].caldav; this.elementCardDavServer.value = tbSync.dav.serviceproviders[serviceprovider].carddav; } } + + this.validating = false; + document.getElementById("tbsync.spinner").hidden = true; + document.getElementById("tbsync.error").hidden = true; }, onUnload: function () { @@ -148,49 +161,146 @@ document.documentElement.getButton("finish").disabled = (this.elementServer.value.trim() + this.elementCalDavServer.value.trim() + this.elementCardDavServer.value.trim() == "" || this.elementName.value.trim() == "" || this.elementUser.value == "" || this.elementPass.value == ""); }, - onAdd: function () { - //window.alert(tbSync.getLocalizedMessage()); - //return false; - + onFinish: function () { if (document.documentElement.getButton("finish").disabled == false) { - let user = this.elementUser.value; - let password = this.elementPass.value; + //initiate validation of server connection, + document.getElementById("tbsync.newaccount.wizard").canRewind = false; + document.documentElement.getButton("finish").disabled = true; + this.validating = true; + this.validate(); + } + return false; + }, + + validate: Task.async (function* () { + document.getElementById("tbsync.error").hidden = true; + document.getElementById("tbsync.spinner").hidden = false; + + this.accountdata = {}; + this.accountdata.accountname = this.elementName.value.trim(); + this.accountdata.user = this.elementUser.value; + this.accountdata.password = this.elementPass.value; + this.accountdata.caldavserver = this.elementCalDavServer.value.trim(); + this.accountdata.carddavserver = this.elementCardDavServer.value.trim(); + + this.accountdata.serviceprovider = this.serviceproviderlist.value; + if (this.accountdata.serviceprovider == "discovery") { + this.accountdata.serviceprovider = "custom"; let server = this.elementServer.value.trim(); while (server.endsWith("/")) { server = server.slice(0,-1); } - let caldavserver = this.elementCalDavServer.value.trim(); - let carddavserver = this.elementCardDavServer.value.trim(); - let accountname = this.elementName.value.trim(); - let serviceprovider = this.serviceproviderlist.value; - - if (serviceprovider == "discovery") { - serviceprovider = "custom"; - caldavserver = server + "/.well-known/caldav"; - carddavserver = server + "/.well-known/carddav"; + this.accountdata.caldavserver = server + "/.well-known/caldav"; + this.accountdata.carddavserver = server + "/.well-known/carddav"; + } else { + while (this.accountdata.caldavserver.endsWith("/")) { this.accountdata.caldavserver = this.accountdata.caldavserver.slice(0,-1); } + while (this.accountdata.carddavserver.endsWith("/")) { this.accountdata.carddavserver = this.accountdata.carddavserver.slice(0,-1); } + } + + //HTTP or HTTPS? Default to https, if http is not explicitly specified + this.accountdata.https = (this.accountdata.caldavserver.toLowerCase().substring(0,7) == "http://") ? "0" : "1"; + this.accountdata.caldavserver = this.accountdata.caldavserver.replace("https://","").replace("http://",""); + this.accountdata.carddavserver = this.accountdata.carddavserver.replace("https://","").replace("http://",""); + + let authenticationManager = Components.classes["@mozilla.org/network/http-auth-manager;1"].getService(Components.interfaces.nsIHttpAuthManager); + let davjobs = { + cal : {valid: false, error: "", server: this.accountdata.caldavserver}, + card : {valid: false, error: "", server: this.accountdata.carddavserver}, + }; + + for (let job in davjobs) { + if (!davjobs[job].server) { + davjobs[job].valid = true; + continue; } - tbSyncDavNewAccount.addAccount(user, password, serviceprovider, caldavserver, carddavserver, accountname); + + let connection = {}; + connection.password = this.accountdata.password; + connection.user = this.accountdata.user; + connection.https = this.accountdata.https; + connection.timeout = 15000; + connection.type = job; + connection.fqdn = ""; + //only needed for proper error reporting + connection.provider = "dav"; + connection.accountname = this.accountdata.accountname; + + //build full url, so we do not need fqdn + let url = "http" + (connection.https == "1" ? "s" : "") + "://" + davjobs[job].server; + + //clear credential cache, so the Channel will call nsIAuthPrompt2 and expose the realm (caldav and carddav could be on the same host but use different realms, so we reset for each type) + authenticationManager.clearAll(); + + try { + let response = yield tbSync.dav.tools.sendRequest("", url , "PROPFIND", connection, {"Depth": "0", "Prefer": "return-minimal"}); + let principal = (response && response.multi) ? tbSync.dav.tools.getNodeTextContentFromMultiResponse(response, [["d","prop"], ["d","current-user-principal"], ["d","href"]]) : null; + davjobs[job].valid = (principal !== null); + if (!davjobs[job].valid) { + davjobs[job].error = job+"davservernotfound"; + } + } catch (e) { + davjobs[job].valid = false; + davjobs[job].error = e.message; + if (e.type == "dav4tbsync") { + tbSync.errorlog("warning", connection, e.message, e.details ? e.details : null); + } else { + Components.utils.reportError(e); + } + } + } + + if (davjobs.cal.valid && davjobs.card.valid) { + tbSyncDavNewAccount.addAccount(this.accountdata); + this.validating = false; + document.getElementById("tbsync.newaccount.wizard").cancel(); } else { - return false; + //only display one error + let badjob = !davjobs.cal.valid ? "cal" : "card"; + switch (davjobs[badjob].error.toString().split("::")[0]) { + case "401": + case "403": + case "404": + case "500": + case "503": + case "network": + case "security": + document.getElementById("tbsync.error.message").textContent = tbSync.getLocalizedMessage("info.error") + ": " + tbSync.getLocalizedMessage("status."+davjobs[badjob].error, "dav"); + break; + default: + document.getElementById("tbsync.error.message").textContent = tbSync.getLocalizedMessage("info.error") + ": " + tbSync.getLocalizedMessage("status.networkerror", "dav"); + } + + document.getElementById("tbsync.spinner").hidden = true; + document.getElementById("tbsync.error").hidden = false; + document.getElementById("tbsync.newaccount.wizard").canRewind = true; + document.documentElement.getButton("finish").disabled = false; + this.validating = false; } + }), + + onClose: function () { + //disallow closing of wizard while validating + return !this.validating; }, - addAccount (user, password, serviceprovider, caldavserver, carddavserver, accountname) { + onCancel: function () { + //disallow closing of wizard while validating + return !this.validating; + }, + + + addAccount (accountdata) { let newAccountEntry = tbSync.dav.getDefaultAccountEntries(); - newAccountEntry.accountname = accountname; - newAccountEntry.user = user; + newAccountEntry.accountname = accountdata.accountname; + newAccountEntry.user = accountdata.user; newAccountEntry.createdWithProviderVersion = tbSync.loadedProviders.dav.version; - //default to https, if not specified - let hasHttp = (caldavserver.substring(0,4) == "http"); - let hasHttps = (caldavserver.substring(0,5) == "https"); - newAccountEntry.https = (!hasHttps && hasHttp) ? "0" : "1"; - - newAccountEntry.serviceprovider = serviceprovider; - newAccountEntry.host = caldavserver.replace("https://","").replace("http://",""); - newAccountEntry.host2 = carddavserver.replace("https://","").replace("http://",""); + newAccountEntry.https = accountdata.https + newAccountEntry.serviceprovider = accountdata.serviceprovider; + newAccountEntry.host = accountdata.caldavserver; + newAccountEntry.host2 = accountdata.carddavserver; //also update password in PasswordManager - tbSync.dav.setPassword (newAccountEntry, password); + tbSync.dav.setPassword (newAccountEntry, accountdata.password); //create a new account and pass its id to updateAccountsList, which will select it //the onSelect event of the List will load the selected account diff -Nru dav4tbsync-0.11/content/manager/createAccount.xul dav4tbsync-0.15/content/manager/createAccount.xul --- dav4tbsync-0.11/content/manager/createAccount.xul 2019-01-31 18:54:04.000000000 +0000 +++ dav4tbsync-0.15/content/manager/createAccount.xul 2019-02-26 16:26:01.000000000 +0000 @@ -1,7 +1,11 @@ - - + + %tbsyncDTD; + + %davDTD; +]>