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;
+]>
-
+
&add.serverprofile.description;
-
+
-
-
+
+
&add.data.description;
@@ -63,7 +68,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff -Nru dav4tbsync-0.11/content/manager/editAccountOverlay.xul dav4tbsync-0.15/content/manager/editAccountOverlay.xul
--- dav4tbsync-0.11/content/manager/editAccountOverlay.xul 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/content/manager/editAccountOverlay.xul 2019-02-26 16:26:01.000000000 +0000
@@ -76,7 +76,6 @@
&pref.syncGroupsDescription;
-
diff -Nru dav4tbsync-0.11/content/overlays/abCardWindow.js dav4tbsync-0.15/content/overlays/abCardWindow.js
--- dav4tbsync-0.11/content/overlays/abCardWindow.js 1970-01-01 00:00:00.000000000 +0000
+++ dav4tbsync-0.15/content/overlays/abCardWindow.js 2019-02-26 16:26:01.000000000 +0000
@@ -0,0 +1,178 @@
+/*
+ * 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";
+
+Components.utils.import("chrome://tbsync/content/tbsync.jsm");
+
+var tbSyncAbDavCardWindow = {
+
+ onBeforeInject: function (window) {
+ let cardProvider = "";
+ let aParentDirURI = "";
+ tbSyncAbDavCardWindow.addressbook = null;
+
+ if (window.location.href=="chrome://messenger/content/addressbook/abNewCardDialog.xul") {
+ aParentDirURI = window.document.getElementById("abPopup").value;
+ } else {
+ aParentDirURI = tbSyncAbDavCardWindow.getSelectedAbFromArgument(window.arguments[0]);
+ }
+
+ if (aParentDirURI) {
+ let folders = tbSync.db.findFoldersWithSetting("target", aParentDirURI);
+ if (folders.length == 1) {
+ cardProvider = tbSync.db.getAccountSetting(folders[0].account, "provider");
+ }
+ }
+
+ //returning false will prevent injection
+ return (cardProvider == "dav");
+ },
+
+ onInject: function (window) {
+ //keep track of default elements we hide/disable, so it can be undone during overlay remove
+ tbSyncAbDavCardWindow.elementsToHide = [];
+ tbSyncAbDavCardWindow.elementsToDisable = [];
+
+ //register default elements we need to hide/disable
+ tbSyncAbDavCardWindow.elementsToHide.push(window.document.getElementById("WorkAddress2Container"));
+ tbSyncAbDavCardWindow.elementsToHide.push(window.document.getElementById("abHomeTab").children[1]);
+ tbSyncAbDavCardWindow.elementsToHide.push(window.document.getElementById("PrimaryEmailContainer"));
+ tbSyncAbDavCardWindow.elementsToHide.push(window.document.getElementById("SecondaryEmailContainer"));
+ tbSyncAbDavCardWindow.elementsToHide.push(window.document.getElementById("PhoneNumbers"));
+
+ //hide stuff from gContactSync *grrrr* - I cannot hide all because he adds them via javascript :-(
+ tbSyncAbDavCardWindow.elementsToHide.push(window.document.getElementById("gContactSyncTab"));
+
+ //hide registered default elements
+ for (let i=0; i < tbSyncAbDavCardWindow.elementsToHide.length; i++) {
+ if (tbSyncAbDavCardWindow.elementsToHide[i]) {
+ tbSyncAbDavCardWindow.elementsToHide[i].collapsed = true;
+ }
+ }
+
+ //disable registered default elements
+ for (let i=0; i < tbSyncAbDavCardWindow.elementsToDisable.length; i++) {
+ if (tbSyncAbDavCardWindow.elementsToDisable[i]) {
+ tbSyncAbDavCardWindow.elementsToDisable[i].disabled = true;
+ }
+ }
+
+ //get current size
+ let currentWidth = window.outerWidth;
+ let currentHeight = window.outerHeight;
+ let newWidth;
+ let newHeight;
+
+ if (window.location.href=="chrome://messenger/content/addressbook/abNewCardDialog.xul") {
+ newWidth = 750;
+ newHeight = 500;
+ window.RegisterSaveListener(tbSyncAbDavCardWindow.onSaveCard);
+ } else {
+ newWidth = 750;
+ newHeight = 450;
+ window.RegisterLoadListener(tbSyncAbDavCardWindow.onLoadCard);
+ window.RegisterSaveListener(tbSyncAbDavCardWindow.onSaveCard);
+
+ //if this window was open during inject, load the extra fields
+ if (gEditCard) tbSyncAbDavCardWindow.onLoadCard(gEditCard.card, window.document);
+ }
+
+ //adjust size if needed
+ if (currentWidth < newWidth || currentHeight < newHeight) {
+ window.resizeTo(Math.max(newWidth, currentWidth), Math.max(newHeight, currentHeight));
+ }
+
+ },
+
+ onRemove: function (window) {
+ if (window.location.href=="chrome://messenger/content/addressbook/abNewCardDialog.xul") {
+ window.UnregisterSaveListener(tbSyncAbDavCardWindow.onSaveCard);
+ } else {
+ window.UnregisterLoadListener(tbSyncAbDavCardWindow.onLoadCard);
+ window.UnregisterSaveListener(tbSyncAbDavCardWindow.onSaveCard);
+ }
+
+ //unhide elements hidden by this provider
+ for (let i=0; i < tbSyncAbDavCardWindow.elementsToHide.length; i++) {
+ if (tbSyncAbDavCardWindow.elementsToHide[i]) {
+ tbSyncAbDavCardWindow.elementsToHide[i].collapsed = false;
+ }
+ }
+
+ //re-enable elements disabled by this provider
+ for (let i=0; i < tbSyncAbDavCardWindow.elementsToDisable.length; i++) {
+ if (tbSyncAbDavCardWindow.elementsToDisable[i]) {
+ tbSyncAbDavCardWindow.elementsToDisable[i].disabled = false;
+ }
+ }
+
+ },
+
+ getSelectedAbFromArgument: function (arg) {
+ let abURI = "";
+ if (arg.hasOwnProperty("abURI")) {
+ abURI = arg.abURI;
+ } else if (arg.hasOwnProperty("selectedAB")) {
+ abURI = arg.selectedAB;
+ }
+
+ if (abURI) {
+ let abManager = Components.classes["@mozilla.org/abmanager;1"].getService(Components.interfaces.nsIAbManager);
+ tbSyncAbDavCardWindow.addressbook = abManager.getDirectory(abURI);
+ if (tbSyncAbDavCardWindow.addressbook.isMailList) {
+ let parts = abURI.split("/");
+ parts.pop();
+ return parts.join("/");
+ }
+ }
+ return abURI;
+ },
+
+ onLoadCard: function (aCard, aDocument) {
+ //load properties
+ let items = aDocument.getElementsByClassName("davProperty");
+ for (let i=0; i < items.length; i++) {
+ items[i].value = aCard.getProperty(items[i].id, "");
+ }
+
+ //get all emails with metadata from card
+ let emails = tbSync.dav.tools.getEmailsFromCard(aCard); //array of objects {meta, value}
+ //add emails to list
+ let emailList = aDocument.getElementById("X-DAV-EmailAddressList");
+ for (let i=0; i < emails.length; i++) {
+ let item = tbSync.dav.tools.getNewEmailListItem(aDocument, emails[i]);
+ emailList.appendChild(item);
+
+ tbSync.dav.tools.updateType(aDocument, tbSync.dav.tools.getEmailListItemElement(item, "button"));
+ tbSync.dav.tools.updatePref(aDocument, tbSync.dav.tools.getEmailListItemElement(item, "pref"));
+ }
+
+ //get all phone numbers with metadata from card
+ let phones = tbSync.dav.tools.getPhoneNumbersFromCard(aCard); //array of objects {meta, value}
+ //add phones to list
+ let phoneList = aDocument.getElementById("X-DAV-PhoneNumberList");
+ for (let i=0; i < phones.length; i++) {
+ let item = tbSync.dav.tools.getNewPhoneListItem(aDocument, phones[i]);
+ phoneList.appendChild(item);
+
+ tbSync.dav.tools.updateType(aDocument, tbSync.dav.tools.getPhoneListItemElement(item, "button1"));
+ tbSync.dav.tools.updateType(aDocument, tbSync.dav.tools.getPhoneListItemElement(item, "button2"));
+ tbSync.dav.tools.updatePref(aDocument, tbSync.dav.tools.getPhoneListItemElement(item, "pref"));
+ }
+
+ },
+
+ onSaveCard: function (aCard, aDocument) {
+ let items = aDocument.getElementsByClassName("davProperty");
+ for (let i=0; i < items.length; i++) {
+ aCard.setProperty(items[i].id, items[i].value);
+ }
+ }
+
+}
diff -Nru dav4tbsync-0.11/content/overlays/abCardWindow.xul dav4tbsync-0.15/content/overlays/abCardWindow.xul
--- dav4tbsync-0.11/content/overlays/abCardWindow.xul 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/content/overlays/abCardWindow.xul 2019-02-26 16:26:01.000000000 +0000
@@ -2,10 +2,53 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -13,13 +56,47 @@
-
+
-
-
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &abCard.emailtypes.description;
+
+
+
+
+
+
diff -Nru dav4tbsync-0.11/content/overlays/abNewCardWindow.js dav4tbsync-0.15/content/overlays/abNewCardWindow.js
--- dav4tbsync-0.11/content/overlays/abNewCardWindow.js 1970-01-01 00:00:00.000000000 +0000
+++ dav4tbsync-0.15/content/overlays/abNewCardWindow.js 2019-02-26 16:26:01.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ * 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";
+
+Components.utils.import("chrome://tbsync/content/tbsync.jsm");
+
+var tbSyncDavAbNewCardWindow = {
+
+ onInject: function (window) {
+ window.document.getElementById("abPopup").addEventListener("select", tbSyncDavAbNewCardWindow.onAbSelectChangeNewCard, false);
+ },
+
+ onRemove: function (window) {
+ window.document.getElementById("abPopup").removeEventListener("select", tbSyncDavAbNewCardWindow.onAbSelectChangeNewCard, false);
+ },
+
+ onAbSelectChangeNewCard: function () {
+ //remove our overlay (if injected)
+ tbSync.dav.overlayManager.removeOverlay(window, "chrome://dav4tbsync/content/overlays/abCardWindow.xul");
+ //inject our overlay (if our card)
+ tbSync.dav.overlayManager.injectOverlay(window, "chrome://dav4tbsync/content/overlays/abCardWindow.xul");
+ },
+
+}
diff -Nru dav4tbsync-0.11/content/overlays/abNewCardWindow.xul dav4tbsync-0.15/content/overlays/abNewCardWindow.xul
--- dav4tbsync-0.11/content/overlays/abNewCardWindow.xul 1970-01-01 00:00:00.000000000 +0000
+++ dav4tbsync-0.15/content/overlays/abNewCardWindow.xul 2019-02-26 16:26:01.000000000 +0000
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff -Nru dav4tbsync-0.11/content/overlays/addressbookdetailsoverlay.js dav4tbsync-0.15/content/overlays/addressbookdetailsoverlay.js
--- dav4tbsync-0.11/content/overlays/addressbookdetailsoverlay.js 1970-01-01 00:00:00.000000000 +0000
+++ dav4tbsync-0.15/content/overlays/addressbookdetailsoverlay.js 2019-02-26 16:26:01.000000000 +0000
@@ -0,0 +1,147 @@
+/*
+ * 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";
+
+Components.utils.import("chrome://tbsync/content/tbsync.jsm");
+
+var tbSyncDavAddressBookDetails = {
+
+ onBeforeInject: function (window) {
+ let cardProvider = "";
+
+ try {
+ let aParentDirURI = window.GetSelectedDirectory();
+ let abManager = Components.classes["@mozilla.org/abmanager;1"].getService(Components.interfaces.nsIAbManager);
+ tbSyncDavAddressBookDetails.selectedBook = abManager.getDirectory(aParentDirURI);
+ if (tbSyncDavAddressBookDetails.selectedBook.isMailList) {
+ aParentDirURI = aParentDirURI.substring(0, aParentDirURI.lastIndexOf("/"));
+ }
+
+ if (aParentDirURI) {
+ let folders = tbSync.db.findFoldersWithSetting("target", aParentDirURI);
+ if (folders.length == 1) {
+ cardProvider = tbSync.db.getAccountSetting(folders[0].account, "provider");
+ }
+ }
+ } catch (e) {
+ //if the window / gDirTree is not yet avail
+ }
+
+ //returning false will prevent injection
+ return (cardProvider == "dav");
+ },
+
+ onInject: function (window) {
+ if (window.document.getElementById("abResultsTree")) {
+ window.document.getElementById("abResultsTree").addEventListener("select", tbSyncDavAddressBookDetails.onAbResultSelectionChanged, false);
+ tbSyncDavAddressBookDetails.onAbResultSelectionChanged();
+ }
+ },
+
+ onRemove: function (window) {
+ tbSyncDavAddressBookDetails.undoChangesToDefaults();
+ if (window.document.getElementById("abResultsTree")) {
+ window.document.getElementById("abResultsTree").removeEventListener("select", tbSyncDavAddressBookDetails.onAbResultSelectionChanged, false);
+ }
+ },
+
+ undoChangesToDefaults: function () {
+ //unhide elements hidden by this provider
+ if (tbSyncDavAddressBookDetails.hasOwnProperty("elementsToHide")) {
+ for (let i=0; i < tbSyncDavAddressBookDetails.elementsToHide.length; i++) {
+ if (tbSyncDavAddressBookDetails.elementsToHide[i]) {
+ tbSyncDavAddressBookDetails.elementsToHide[i].hidden = false;
+ }
+ }
+ }
+
+ //re-enable elements disabled by this provider
+ if (tbSyncDavAddressBookDetails.hasOwnProperty("elementsToDisable")) {
+ for (let i=0; i < tbSyncDavAddressBookDetails.elementsToDisable.length; i++) {
+ if (tbSyncDavAddressBookDetails.elementsToDisable[i]) {
+ tbSyncDavAddressBookDetails.elementsToDisable[i].disabled = false;
+ }
+ }
+ }
+
+ tbSyncDavAddressBookDetails.elementsToHide = [];
+ tbSyncDavAddressBookDetails.elementsToDisable = [];
+ },
+
+ onAbResultSelectionChanged: function () {
+ tbSyncDavAddressBookDetails.undoChangesToDefaults();
+
+ let cards = window.GetSelectedAbCards();
+ if (cards.length == 1) {
+ let aCard = cards[0];
+
+ //add emails
+ let emails = tbSync.dav.tools.getEmailsFromCard(aCard); //array of objects {meta, value}
+ let emailDetails = window.document.getElementById("cvbEmailRows");
+ if (emailDetails) {
+ //remove all rows
+ while (emailDetails.firstChild) {
+ emailDetails.removeChild(emailDetails.firstChild);
+ }
+
+ for (let i=0; i < emails.length; i++) {
+ emailDetails.appendChild(tbSync.dav.tools.getNewEmailDetailsRow(window, emails[i]));
+ }
+
+ if (window.document.getElementById("cvbEmails")) {
+ window.document.getElementById("cvbEmails").collapsed = (emails.length == 0);
+ }
+ }
+
+ //add phone numbers
+ let phones = tbSync.dav.tools.getPhoneNumbersFromCard(aCard); //array of objects {meta, value}
+ let phoneDetails = window.document.getElementById("cvbPhoneRows");
+ if (phoneDetails) {
+ //remove all rows
+ while (phoneDetails.firstChild) {
+ phoneDetails.removeChild(phoneDetails.firstChild);
+ }
+
+ for (let i=0; i < phones.length; i++) {
+ phoneDetails.appendChild(tbSync.dav.tools.getNewPhoneDetailsRow(window, phones[i]));
+ }
+
+ if (window.document.getElementById("cvbPhoneNumbers")) {
+ window.document.getElementById("cvbPhoneNumbers").collapsed = (phones.length == 0);
+ }
+ }
+
+
+ //hide primary and secondary email
+ if (!tbSyncDavAddressBookDetails.hasOwnProperty("elementsToHide")) tbSyncDavAddressBookDetails.elementsToHide = [];
+ if (!tbSyncDavAddressBookDetails.hasOwnProperty("elementsToDisable")) tbSyncDavAddressBookDetails.elementsToDisable = [];
+ tbSyncDavAddressBookDetails.elementsToHide.push(window.document.getElementById("cvEmail1Box"));
+ tbSyncDavAddressBookDetails.elementsToHide.push(window.document.getElementById("cvEmail2Box"));
+ tbSyncDavAddressBookDetails.elementsToHide.push(window.document.getElementById("cvbPhone"));
+
+ //hide registered default elements
+ for (let i=0; i < tbSyncDavAddressBookDetails.elementsToHide.length; i++) {
+ if (tbSyncDavAddressBookDetails.elementsToHide[i]) {
+ tbSyncDavAddressBookDetails.elementsToHide[i].hidden = true;
+ //using "hidden" and not "collapsed", because TB is flipping collapsed itself after the card has been edited/saved
+ //and if we also use that property, the fields "blink" for a split second. Using "hidden" the field stays hidden even if TB is uncollapsing
+ }
+ }
+
+ //disable registered default elements
+ for (let i=0; i < tbSyncDavAddressBookDetails.elementsToDisable.length; i++) {
+ if (tbSyncDavAddressBookDetails.elementsToDisable[i]) {
+ tbSyncDavAddressBookDetails.elementsToDisable[i].disabled = true;
+ }
+ }
+
+ }
+ },
+
+}
diff -Nru dav4tbsync-0.11/content/overlays/addressbookdetailsoverlay.xul dav4tbsync-0.15/content/overlays/addressbookdetailsoverlay.xul
--- dav4tbsync-0.11/content/overlays/addressbookdetailsoverlay.xul 1970-01-01 00:00:00.000000000 +0000
+++ dav4tbsync-0.15/content/overlays/addressbookdetailsoverlay.xul 2019-02-26 16:26:01.000000000 +0000
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ &abCard.EmailAddresses;
+
+
+
+
+
+
+
+
+
+
+
+ &abCard.Phone;
+
+
+
+
+
+
+
+
+
+
+
diff -Nru dav4tbsync-0.11/content/overlays/addressbookoverlay.js dav4tbsync-0.15/content/overlays/addressbookoverlay.js
--- dav4tbsync-0.11/content/overlays/addressbookoverlay.js 1970-01-01 00:00:00.000000000 +0000
+++ dav4tbsync-0.15/content/overlays/addressbookoverlay.js 2019-02-26 16:26:01.000000000 +0000
@@ -0,0 +1,42 @@
+/*
+ * 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";
+
+Components.utils.import("chrome://tbsync/content/tbsync.jsm");
+
+var tbSyncDavAddressBook = {
+
+ onInject: function (window) {
+ Services.obs.addObserver(tbSyncDavAddressBook.onAddressBookCreated, "tbsync.addressbook.created", false);
+ if (window.document.getElementById("dirTree")) {
+ window.document.getElementById("dirTree").addEventListener("select", tbSyncDavAddressBook.onAbDirectorySelectionChanged, false);
+ }
+ },
+
+ onRemove: function (window) {
+ Services.obs.removeObserver(tbSyncDavAddressBook.onAddressBookCreated, "tbsync.addressbook.created");
+ if (window.document.getElementById("dirTree")) {
+ window.document.getElementById("dirTree").removeEventListener("select", tbSyncDavAddressBook.onAbDirectorySelectionChanged, false);
+ }
+ },
+
+ onAddressBookCreated: {
+ observe: function (aSubject, aTopic, aData) {
+ tbSyncDavAddressBook.onAbDirectorySelectionChanged();
+ }
+ },
+
+ onAbDirectorySelectionChanged: function () {
+ //TODO: Do not do this, if provider did not change
+ //remove our details injection (if injected)
+ tbSync.dav.overlayManager.removeOverlay(window, "chrome://dav4tbsync/content/overlays/addressbookdetailsoverlay.xul");
+ //inject our details injection (if the new selected book is us)
+ tbSync.dav.overlayManager.injectOverlay(window, "chrome://dav4tbsync/content/overlays/addressbookdetailsoverlay.xul");
+ }
+}
diff -Nru dav4tbsync-0.11/content/overlays/addressbookoverlay.xul dav4tbsync-0.15/content/overlays/addressbookoverlay.xul
--- dav4tbsync-0.11/content/overlays/addressbookoverlay.xul 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/content/overlays/addressbookoverlay.xul 2019-02-26 16:26:01.000000000 +0000
@@ -1,11 +1,12 @@
-
+
+ id="TbSyncAbDavOverlay"
+ oninject="tbSyncDavAddressBook.onInject(window)"
+ onremove="tbSyncDavAddressBook.onRemove(window)"
+ xmlns:html="http://www.w3.org/1999/xhtml">
-
-
+
diff -Nru dav4tbsync-0.11/content/sync.js dav4tbsync-0.15/content/sync.js
--- dav4tbsync-0.11/content/sync.js 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/content/sync.js 2019-02-26 16:26:01.000000000 +0000
@@ -53,30 +53,25 @@
unhandledFolders[folders[f].type].push(f);
}
- let davjobs = {
- card : {run: true},
- cal : {run: tbSync.lightningIsAvailable()},
- };
-
//get server urls from account setup - update urls of serviceproviders
let serviceprovider = tbSync.db.getAccountSetting(syncdata.account, "serviceprovider");
if (tbSync.dav.serviceproviders.hasOwnProperty(serviceprovider)) {
tbSync.db.setAccountSetting(syncdata.account, "host", tbSync.dav.serviceproviders[serviceprovider].caldav.replace("https://","").replace("http://",""));
tbSync.db.setAccountSetting(syncdata.account, "host2", tbSync.dav.serviceproviders[serviceprovider].carddav.replace("https://","").replace("http://",""));
}
- davjobs.cal.initialURL = tbSync.db.getAccountSetting(syncdata.account, "host");
- davjobs.card.initialURL = tbSync.db.getAccountSetting(syncdata.account, "host2");
+
+ let davjobs = {
+ cal : {server: tbSync.db.getAccountSetting(syncdata.account, "host")},
+ card : {server: tbSync.db.getAccountSetting(syncdata.account, "host2")},
+ };
let authenticationManager = Components.classes["@mozilla.org/network/http-auth-manager;1"].getService(Components.interfaces.nsIHttpAuthManager);
for (let job in davjobs) {
- if (!davjobs[job].run || !davjobs[job].initialURL) continue;
+ if (!davjobs[job].server) continue;
//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();
-
- //keep track of the current job
- syncdata.type = job;
//sync states are only printed while the account state is "syncing" to inform user about sync process (it is not stored in DB, just in syncdata)
//example state "getfolders" to get folder information from server
@@ -89,12 +84,16 @@
tbSync.setSyncState("send.getfolders", syncdata.account);
{
- //split initialURL into host and url
- let parts = davjobs[job].initialURL.split("/").filter(i => i != "");
+ //split server into fqdn and path
+ let parts = davjobs[job].server.split("/").filter(i => i != "");
+
+ //add connection data to syncdata
syncdata.fqdn = parts.splice(0,1).toString();
- let addr = "/" + parts.join("/");
-
- let response = yield dav.tools.sendRequest("", addr , "PROPFIND", syncdata, {"Depth": "0", "Prefer": "return-minimal"});
+ syncdata.type = job;
+ dav.tools.addAccountDataToConnectionData(syncdata);
+
+ let path = "/" + parts.join("/");
+ let response = yield dav.tools.sendRequest("", path , "PROPFIND", syncdata, {"Depth": "0", "Prefer": "return-minimal"});
tbSync.setSyncState("eval.folders", syncdata.account);
if (response && response.multi) principal = dav.tools.getNodeTextContentFromMultiResponse(response, [["d","prop"], ["d","current-user-principal"], ["d","href"]]);
@@ -123,14 +122,18 @@
//Any groups we need to find? Only diving one level at the moment,
let g = dav.tools.getNodesTextContentFromMultiResponse(response, [["d","prop"], ["d", "group-membership" ], ["d","href"]], principal);
for (let gc=0; gc < g.length; gc++) {
- response = yield dav.tools.sendRequest(request, g[gc], "PROPFIND", syncdata, {"Depth": "0", "Prefer": "return-minimal"});
+ //SOGo reports a 403 if I request the provided resource, also since we do not dive, remove the request for group-membership
+ response = yield dav.tools.sendRequest(request.replace("",""), g[gc], "PROPFIND", syncdata, {"Depth": "0", "Prefer": "return-minimal"}, {softfail: [403, 404]});
+ if (response && response.softerror) {
+ continue;
+ }
home = home.concat(dav.tools.getNodesTextContentFromMultiResponse(response, [["d","prop"], [job, homeset ], ["d","href"]], g[gc]));
}
//calendar-proxy and group-membership could have returned the same values, make the homeset unique
home = home.filter((v,i,a) => a.indexOf(v) == i);
} else {
- throw dav.sync.failed(job+"davservernotfound", davjobs[job].initialURL)
+ throw dav.sync.failed(job+"davservernotfound", davjobs[job].server)
}
//home now contains something like /remote.php/caldav/calendars/john.bieling/
@@ -143,7 +146,7 @@
: "";
//some servers report to have calendar-proxy-read but return a 404 when that gets actually queried
- let response = yield dav.tools.sendRequest(request, home[h], "PROPFIND", syncdata, {"Depth": "1", "Prefer": "return-minimal"}, {softfail: [404]});
+ let response = yield dav.tools.sendRequest(request, home[h], "PROPFIND", syncdata, {"Depth": "1", "Prefer": "return-minimal"}, {softfail: [403, 404]});
if (response && response.softerror) {
continue;
}
@@ -190,7 +193,7 @@
if (resourcetype == "ics") href = dav.tools.evaluateNode(response.multi[r].node, [["d","prop"], ["cs","source"], ["d","href"]]).textContent;
let name_node = dav.tools.evaluateNode(response.multi[r].node, [["d","prop"], ["d","displayname"]]);
- let name = (job == "cal") ? "Calendar" : "Contacts";
+ let name = tbSync.getLocalizedMessage("defaultname." + ((job == "cal") ? "calendar" : "contacts") , "dav");
if (name_node != null) {
name = name_node.textContent;
}
@@ -211,6 +214,7 @@
newFolder.parentID = "0"; //root - tbsync flatens hierachy, using parentID to sort entries
newFolder.selected = "0";
+ //we assume the folder has the same fqdn as the homeset, otherwise the folderID must contain the full URL and the fqdn is ignored
newFolder.fqdn = syncdata.fqdn;
//if there is a cached version of this folderID, addFolder will merge all persistent settings - all other settings not defined here will be set to their defaults
@@ -287,11 +291,15 @@
//all folders of this account have been synced
throw dav.sync.succeeded();
}
+
//what folder are we syncing?
syncdata.folderID = nextFolder.folderID;
+
+ //add connection data to syncdata
syncdata.type = nextFolder.type;
syncdata.fqdn = nextFolder.fqdn;
-
+ dav.tools.addAccountDataToConnectionData(syncdata);
+
try {
switch (syncdata.type) {
case "carddav":
@@ -363,7 +371,7 @@
singleFolder: Task.async (function* (syncdata) {
syncdata.downloadonly = (tbSync.db.getFolderSetting(syncdata.account, syncdata.folderID, "downloadonly") == "1");
syncdata.folderCreatedWithProviderVersion = tbSync.db.getFolderSetting(syncdata.account, syncdata.folderID, "createdWithProviderVersion");
-
+
yield dav.sync.remoteChanges(syncdata);
let numOfLocalChanges = yield dav.sync.localChanges(syncdata);
@@ -421,7 +429,7 @@
let token = tbSync.db.getFolderSetting(syncdata.account, syncdata.folderID, "token");
tbSync.setSyncState("send.request.remotechanges", syncdata.account, syncdata.folderID);
- let cards = yield dav.tools.sendRequest(""+token+"1", syncdata.folderID, "REPORT", syncdata, {"Content-Type": "application/xml; charset=utf-8"}, {softfail: [415,403]});
+ let cards = yield dav.tools.sendRequest(""+token+"1", syncdata.folderID, "REPORT", syncdata, {}, {softfail: [415,403]});
//Sabre\DAV\Exception\ReportNotSupported - Unsupported media type - returned by fruux if synctoken is 0 (empty book), 415 & 403
//https://github.com/sabre-io/dav/issues/1075
@@ -447,9 +455,8 @@
let id = cards.multi[c].href;
if (id !==null) {
//valid
- let status = cards.multi[c].status;
let card = tbSync.getCardFromProperty(addressBook, "TBSYNCID", id);
- if (status == "200") {
+ if (cards.multi[c].status == "200") {
//MOD or ADD
let etag = dav.tools.evaluateNode(cards.multi[c].node, [["d","prop"], ["d","getetag"]]);
if (!card) {
@@ -462,11 +469,14 @@
syncdata.todo++;
vCardsChangedOnServer[id] = "MOD";
}
- } else if (status == "404" && card) {
+ } else if (cards.multi[c].responsestatus == "404" && card) {
//DEL
syncdata.todo++;
vCardsDeletedOnServer.appendElement(card, false);
tbSync.db.addItemToChangeLog(syncdata.targetId, id, "deleted_by_server");
+ } else {
+ //We received something, that is not a DEL, MOD or ADD
+ tbSync.errorlog("warning", syncdata, "Unknown XML", JSON.stringify(cards.multi[c]));
}
}
}
@@ -595,7 +605,7 @@
let request = dav.tools.getMultiGetRequest(cards2catch.slice(i, i+maxitems));
if (request) {
tbSync.setSyncState("send.request.remotechanges", syncdata.account, syncdata.folderID);
- let cards = yield dav.tools.sendRequest(request, syncdata.folderID, "REPORT", syncdata, {"Depth": "1", "Content-Type": "application/xml; charset=utf-8"});
+ let cards = yield dav.tools.sendRequest(request, syncdata.folderID, "REPORT", syncdata, {"Depth": "1"});
tbSync.setSyncState("eval.response.remotechanges", syncdata.account, syncdata.folderID);
for (let c=0; c < cards.multi.length; c++) {
@@ -614,6 +624,9 @@
dav.tools.modifyContact (addressBook, id, data, etag, syncdata);
break;
}
+ //Feedback from users: They want to see the individual count
+ tbSync.setSyncState("eval.response.remotechanges", syncdata.account, syncdata.folderID);
+ yield tbSync.sleep(100, false);
} else {
tbSync.dump("Skipped Card", [id, cards.multi[c].status == "200", etag !== null, data !== null, id !== null, vCardsChangedOnServer.hasOwnProperty(id)].join(", "));
}
@@ -621,8 +634,8 @@
}
}
//Feedback from users: They want to see the final count
- tbSync.setSyncState("eval.request.remotechanges", syncdata.account, syncdata.folderID);
- yield tbSync.sleep(100, false);
+ tbSync.setSyncState("eval.response.remotechanges", syncdata.account, syncdata.folderID);
+ yield tbSync.sleep(200, false);
let syncGroups = (tbSync.db.getAccountSetting(syncdata.account, "syncGroups") == "1");
if (syncGroups) {
@@ -674,7 +687,9 @@
//fix for bug 1522453
let email = card.getProperty("PrimaryEmail", "");
if (!email) {
- card.setProperty("PrimaryEmail", Date.now() + "." +listcount + "." + i + "@bug1522453");
+ let email = Date.now() + "." +listcount + "." + i + "@bug1522453";
+ card.setProperty("PrimaryEmail", email);
+ card.setProperty("X-DAV-JSON-Emails", JSON.stringify([{meta: [], value: email}]));
addressBook.modifyCard(card);
}
mailListDirectory.addressLists.appendElement(card, false);
@@ -819,12 +834,14 @@
{
if (!permissionError[changes[i].status]) { //if this operation failed already, do not retry
tbSync.setSyncState("send.request.localchanges", syncdata.account, syncdata.folderID);
- let response = yield dav.tools.sendRequest("", changes[i].id , "DELETE", syncdata, {}, {softfail: [403, 405]});
+ let response = yield dav.tools.sendRequest("", changes[i].id , "DELETE", syncdata, {}, {softfail: [403, 404, 405]});
tbSync.setSyncState("eval.response.localchanges", syncdata.account, syncdata.folderID);
if (response && response.softerror) {
- permissionError[changes[i].status] = true;
- tbSync.errorlog("warning", syncdata, "missing-permission::" + tbSync.getLocalizedMessage("acl.delete", "dav"));
+ if (response.softerror != 404) { //we cannot do anything about a 404 on delete, the card has been deleted here and is not avail on server
+ permissionError[changes[i].status] = true;
+ tbSync.errorlog("warning", syncdata, "missing-permission::" + tbSync.getLocalizedMessage("acl.delete", "dav"));
+ }
}
}
diff -Nru dav4tbsync-0.11/content/tools.js dav4tbsync-0.15/content/tools.js
--- dav4tbsync-0.11/content/tools.js 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/content/tools.js 2019-02-26 16:26:01.000000000 +0000
@@ -9,7 +9,490 @@
"use strict";
dav.tools = {
+
+ //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ //* Functions to handle advanced UI elements of AB
+ //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+ updatePref: function(aDocument, icon, toggle = false) {
+ if (toggle) {
+ if (icon.parentNode.meta.includes("PREF")) icon.parentNode.meta = icon.parentNode.meta.filter(e => e != "PREF");
+ else icon.parentNode.meta.push("PREF");
+
+ icon.parentNode.updateFunction (aDocument);
+ }
+
+ if (icon.parentNode.meta.includes("PREF")) {
+ icon.setAttribute("src", "chrome://dav4tbsync/skin/type.pref.png");
+ } else {
+ icon.setAttribute("src", "chrome://dav4tbsync/skin/type.nopref.png");
+ }
+ },
+
+ updateType: function(aDocument, button, newvalue = null) {
+ if (newvalue) {
+ //we declare allowedValues to be non-overlapping -> remove all allowed values and just add the newvalue
+ button.parentNode.meta = button.parentNode.meta.filter(value => -1 == button.allowedValues.indexOf(value));
+ if (button.allowedValues.includes(newvalue)) button.parentNode.meta.push(newvalue);
+
+ button.parentNode.updateFunction (aDocument);
+ }
+
+ let intersection = button.parentNode.meta.filter(value => -1 !== button.allowedValues.indexOf(value));
+ let buttonType = (intersection.length > 0) ? intersection[0].toLowerCase() : button.otherIcon;
+ button.setAttribute("image","chrome://dav4tbsync/skin/type."+buttonType+"10.png");
+ },
+
+
+ //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ //* Functions to handle multiple email addresses in AB (UI)
+ //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+ getEmailsFromCard: function (aCard) { //return array of objects {meta, value}
+ let emails = aCard.getProperty("X-DAV-JSON-Emails","").trim();
+ if (emails) {
+ return JSON.parse(emails);
+ }
+
+ emails = [];
+
+ //There is no X-DAV-JSON-Emails property (an empty JSON would not be "")
+ //Is there a stored VCARD we can fallback to?
+ let storedCard = tbSync.getPropertyOfCard(aCard, "X-DAV-VCARD").trim();
+ let sCardData = tbSync.dav.vCard.parse(storedCard);
+ if (sCardData.hasOwnProperty("email")) {
+ let metaTypeData = dav.tools.getMetaTypeData(sCardData, "email", "type");
+ for (let i=0; i < metaTypeData.length; i++) {
+ emails.push({value: sCardData["email"][i].value, meta: metaTypeData[i]});
+ }
+ return emails;
+ }
+
+ //So this card is not a "DAV" card: Get the emails from current emails stored in
+ //PrimaryEmail and SecondEmail
+ for (let e of ["PrimaryEmail", "SecondEmail"]) {
+ let email = aCard.getProperty(e,"").trim();
+ if (email) {
+ emails.push({value: email, meta: []});
+ }
+ }
+ return emails;
+ },
+
+ getEmailsFromJSON: function (emailDataJSON) {
+ let emailFields = {};
+
+ if (emailDataJSON) {
+ try {
+ //we pack the first entry into PrimaryEmail and all other into SecondEmail
+ let emailData = JSON.parse(emailDataJSON);
+ emailFields = {PrimaryEmail:[], SecondEmail:[]};
+
+ for (let d=0; d < emailData.length; d++) {
+ let field = (d==0) ? "PrimaryEmail" : "SecondEmail";
+ emailFields[field].push(emailData[d].value);
+ }
+ } catch(e) {
+ //something went wrong
+ Components.utils.reportError(e);
+ }
+ }
+
+ //object with TB field names as keys and array of numbers as values
+ return emailFields;
+ },
+
+
+ getNewEmailDetailsRow: function (aWindow, aItemData) {
+ let emailType = "other";
+ if (aItemData.meta.includes("HOME")) emailType = "home";
+ else if (aItemData.meta.includes("WORK")) emailType = "work";
+
+ //first column
+ let vbox = aWindow.document.createElement("vbox");
+ vbox.setAttribute("class","CardViewText");
+ vbox.setAttribute("style","margin-right:1ex; margin-bottom:2px;");
+ let image = aWindow.document.createElement("image");
+ image.setAttribute("width","10");
+ image.setAttribute("height","10");
+ image.setAttribute("src", "chrome://dav4tbsync/skin/type."+emailType+"10.png");
+ vbox.appendChild(image);
+
+ //second column
+ let description = aWindow.document.createElement("description");
+ description.setAttribute("class","plain");
+ let namespace = aWindow.document.lookupNamespaceURI("html");
+ let a = aWindow.document.createElementNS(namespace, "a");
+ a.setAttribute("href", "mailto:" + aItemData.value);
+ a.textContent = aItemData.value;
+ description.appendChild(a);
+
+ if (aItemData.meta.includes("PREF")) {
+ let pref = aWindow.document.createElement("image");
+ pref.setAttribute("style", "margin-left:1ex;");
+ pref.setAttribute("width", "11");
+ pref.setAttribute("height", "10");
+ pref.setAttribute("src", "chrome://dav4tbsync/skin/type.nopref.png");
+ description.appendChild(pref);
+ }
+
+ //row
+ let row = aWindow.document.createElement("row");
+ row.setAttribute("align","end");
+ row.appendChild(vbox);
+ row.appendChild(description);
+ return row;
+ },
+
+ getNewEmailListItem: function (aDocument, aItemData) {
+ //hbox
+ let outerhbox = aDocument.createElement("hbox");
+ outerhbox.setAttribute("flex", "1");
+ outerhbox.setAttribute("align", "center");
+ outerhbox.updateFunction = dav.tools.updateEmails;
+ outerhbox.meta = aItemData.meta;
+
+ //button
+ let button = aDocument.createElement("button");
+ button.allowedValues = ["HOME", "WORK"];
+ button.otherIcon = "other";
+ button.setAttribute("type", "menu");
+ button.setAttribute("class", "plain");
+ button.setAttribute("style", "width: 35px; min-width: 35px; margin: 0;");
+ button.appendChild(aDocument.getElementById("DavEmailSpacer").children[0].cloneNode(true));
+ outerhbox.appendChild(button);
+
+ //email box
+ let emailbox = aDocument.createElement("hbox");
+ emailbox.setAttribute("flex", "1");
+ emailbox.setAttribute("style", "padding-bottom:1px");
+ let email = aDocument.createElement("textbox");
+ email.setAttribute("flex", "1");
+ email.setAttribute("class", "plain");
+ email.setAttribute("value", aItemData.value);
+ email.addEventListener("change", function(e) {tbSync.dav.tools.updateEmails(aDocument)});
+ emailbox.appendChild(email);
+ outerhbox.appendChild(emailbox);
+
+ //image
+ let image = aDocument.createElement("image");
+ image.setAttribute("width", "11");
+ image.setAttribute("height", "10");
+ image.setAttribute("style", "margin:2px 2px 2px 1ex");
+ image.addEventListener("click", function(e) { tbSync.dav.tools.updatePref(aDocument, e.target, true); });
+ outerhbox.appendChild(image);
+
+ //richlistitem
+ let richlistitem = aDocument.createElement("richlistitem");
+ richlistitem.appendChild(outerhbox);
+
+ return richlistitem;
+ },
+
+ getEmailListItemElement: function(item, element) {
+ switch (element) {
+ case "dataContainer":
+ return item.children[0];
+ case "button":
+ return item.children[0].children[0];
+ case "email":
+ return item.children[0].children[1].children[0];
+ case "pref":
+ return item.children[0].children[2];
+ default:
+ return null;
+ }
+ },
+
+ addEmailEntry: function(aDocument) {
+ let list = aDocument.getElementById("X-DAV-EmailAddressList");
+ let data = {value: "", meta: ["HOME"]};
+ let item = dav.tools.getNewEmailListItem(aDocument, data);
+ list.ensureElementIsVisible(list.appendChild(item));
+
+ dav.tools.updateType(aDocument, dav.tools.getEmailListItemElement(item, "button"));
+ dav.tools.updatePref(aDocument, dav.tools.getEmailListItemElement(item, "pref"));
+ },
+
+
+ //if any setting changed, we need to update Primary and Secondary Email Fields
+ updateEmails: function(aDocument) {
+ let list = aDocument.getElementById("X-DAV-EmailAddressList");
+
+ let emails = [];
+ for (let i=0; i < list.children.length; i++) {
+ let item = list.children[i];
+ let email = dav.tools.getEmailListItemElement(item, "email").value.trim();
+ if (email != "") {
+ let json = {};
+ json.meta = dav.tools.getEmailListItemElement(item, "dataContainer").meta;
+ json.value = email;
+ emails.push(json);
+ }
+ }
+ aDocument.getElementById("X-DAV-JSON-Emails").value = JSON.stringify(emails);
+
+ //now update all other TB enail fields based on the new JSON data
+ let emailData = dav.tools.getEmailsFromJSON(aDocument.getElementById("X-DAV-JSON-Emails").value);
+ for (let field in emailData) {
+ if (emailData.hasOwnProperty(field)) {
+ aDocument.getElementById(field).value = emailData[field].join(", ");
+ }
+ }
+ },
+
+
+
+
+ //* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ //* Functions to handle multiple phone numbers in AB (UI)
+ //* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+ getPhoneNumbersFromCard: function (aCard) { //return array of objects {meta, value}
+ let phones = aCard.getProperty("X-DAV-JSON-Phones","").trim();
+ if (phones) {
+ return JSON.parse(phones);
+ }
+
+ phones = [];
+
+ //There is no X-DAV-JSON-Phones property (an empty JSON would not be "")
+ //Is there a stored VCARD we can fallback to?
+ let storedCard = tbSync.getPropertyOfCard(aCard, "X-DAV-VCARD").trim();
+ let sCardData = tbSync.dav.vCard.parse(storedCard);
+ if (sCardData.hasOwnProperty("tel")) {
+ let metaTypeData = dav.tools.getMetaTypeData(sCardData, "tel", "type");
+ for (let i=0; i < metaTypeData.length; i++) {
+ phones.push({value: sCardData["tel"][i].value, meta: metaTypeData[i]});
+ }
+ return phones;
+ }
+
+ //So this card is not a "DAV" card: Get the phone numbers from current numbers stored in
+ //CellularNumber, FaxNumber, PagerNumber, WorkPhone, HomePhone"},
+ let todo = [
+ {field: "CellularNumber", meta: ["CELL"]},
+ {field: "FaxNumber", meta: ["FAX"]},
+ {field: "PagerNumber", meta: ["PAGER"]},
+ {field: "WorkPhone", meta: ["WORK"]},
+ {field: "HomePhone", meta: ["HOME"]}
+ ];
+
+ for (let data of todo) {
+ let phone = aCard.getProperty(data.field,"").trim();
+ if (phone) {
+ phones.push({value: phone, meta: data.meta});
+ }
+ }
+ return phones;
+ },
+
+ getPhoneNumbersFromJSON: function (phoneDataJSON) {
+ let phoneFields = {};
+
+ if (phoneDataJSON) {
+ try {
+ //we first search and remove CELL, FAX, PAGER and WORK from the list and put the remains into HOME
+ let phoneData = JSON.parse(phoneDataJSON);
+ let phoneMap = [
+ {meta: "CELL", field: "CellularNumber"},
+ {meta: "FAX", field: "FaxNumber"},
+ {meta: "PAGER", field: "PagerNumber"},
+ {meta: "WORK", field: "WorkPhone"},
+ {meta: "", field: "HomePhone"},
+ ];
+
+ for (let m=0; m < phoneMap.length; m++) {
+ phoneFields[phoneMap[m].field] = [];
+ for (let d=phoneData.length-1; d >= 0; d--) {
+ if (phoneData[d].meta.includes(phoneMap[m].meta) || phoneMap[m].meta == "") {
+ phoneFields[phoneMap[m].field].push(phoneData[d].value);
+ phoneData.splice(d,1);
+ }
+ }
+ }
+ } catch(e) {
+ //something went wrong
+ Components.utils.reportError(e);
+ }
+ }
+
+ //object with TB field names as keys and array of numbers as values
+ return phoneFields;
+ },
+
+ getNewPhoneDetailsRow: function (aWindow, aItemData) {
+ let phoneType1 = "";
+ if (aItemData.meta.includes("HOME")) phoneType1 = "home";
+ else if (aItemData.meta.includes("WORK")) phoneType1 = "work";
+
+ let phoneType2 = "";
+ if (aItemData.meta.includes("CELL")) phoneType2 = "cell";
+ else if (aItemData.meta.includes("FAX")) phoneType2 = "fax";
+ else if (aItemData.meta.includes("PAGER")) phoneType2 = "pager";
+ else if (aItemData.meta.includes("CAR")) phoneType2 = "car";
+ else if (aItemData.meta.includes("VIDEO")) phoneType2 = "video";
+ else if (aItemData.meta.includes("VOICE")) phoneType2 = "voice";
+
+ //first column
+ let vbox = aWindow.document.createElement("hbox");
+ vbox.setAttribute("pack","end");
+ vbox.setAttribute("class","CardViewText");
+ vbox.setAttribute("style","margin-bottom:3px;");
+ if (phoneType1) {
+ let image = aWindow.document.createElement("image");
+ image.setAttribute("style","margin-right:1ex;");
+ image.setAttribute("width","10");
+ image.setAttribute("height","10");
+ image.setAttribute("src", "chrome://dav4tbsync/skin/type."+phoneType1+"10.png");
+ vbox.appendChild(image);
+ }
+ if (phoneType2) {
+ let image = aWindow.document.createElement("image");
+ image.setAttribute("style","margin-right:1ex;");
+ image.setAttribute("width","10");
+ image.setAttribute("height","10");
+ image.setAttribute("src", "chrome://dav4tbsync/skin/type."+phoneType2+"10.png");
+ vbox.appendChild(image);
+ }
+
+ //second column
+ let description = aWindow.document.createElement("description");
+ description.setAttribute("class","plain");
+ description.textContent = aItemData.value;
+
+ if (aItemData.meta.includes("PREF")) {
+ let pref = aWindow.document.createElement("image");
+ pref.setAttribute("style", "margin-left:1ex;");
+ pref.setAttribute("width", "11");
+ pref.setAttribute("height", "10");
+ pref.setAttribute("src", "chrome://dav4tbsync/skin/type.nopref.png");
+ description.appendChild(pref);
+ }
+
+ //row
+ let row = aWindow.document.createElement("row");
+ row.setAttribute("align","end");
+ row.appendChild(vbox);
+ row.appendChild(description);
+ return row;
+ },
+
+ getNewPhoneListItem: function (aDocument, aItemData) {
+ //hbox
+ let outerhbox = aDocument.createElement("hbox");
+ outerhbox.setAttribute("flex", "1");
+ outerhbox.setAttribute("align", "center");
+ outerhbox.updateFunction = dav.tools.updatePhoneNumbers;
+ outerhbox.meta = aItemData.meta;
+
+ //button1
+ let button1 = aDocument.createElement("button");
+ button1.allowedValues = ["HOME", "WORK"];
+ button1.otherIcon = "none";
+ button1.setAttribute("type", "menu");
+ button1.setAttribute("class", "plain");
+ button1.setAttribute("style", "width: 35px; min-width: 35px; margin: 0;");
+ button1.appendChild(aDocument.getElementById("DavEmailSpacer").children[1].cloneNode(true));
+ outerhbox.appendChild(button1);
+
+ //button2
+ let button2 = aDocument.createElement("button");
+ button2.allowedValues = ["CELL", "FAX", "PAGER", "CAR", "VIDEO", "VOICE"] ; //same order as in getNewPhoneDetailsRow
+ button2.otherIcon = "none";
+ button2.setAttribute("type", "menu");
+ button2.setAttribute("class", "plain");
+ button2.setAttribute("style", "width: 35px; min-width: 35px; margin: 0;");
+ button2.appendChild(aDocument.getElementById("DavEmailSpacer").children[2].cloneNode(true));
+ outerhbox.appendChild(button2);
+
+ //email box
+ let phonebox = aDocument.createElement("hbox");
+ phonebox.setAttribute("flex", "1");
+ phonebox.setAttribute("style", "padding-bottom:1px");
+ let phone = aDocument.createElement("textbox");
+ phone.setAttribute("flex", "1");
+ phone.setAttribute("class", "plain");
+ phone.setAttribute("value", aItemData.value);
+ phone.addEventListener("change", function(e) {tbSync.dav.tools.updatePhoneNumbers(aDocument)});
+ phonebox.appendChild(phone);
+ outerhbox.appendChild(phonebox);
+
+ //image
+ let image = aDocument.createElement("image");
+ image.setAttribute("width", "11");
+ image.setAttribute("height", "10");
+ image.setAttribute("style", "margin:2px 2px 2px 1ex");
+ image.addEventListener("click", function(e) { tbSync.dav.tools.updatePref(aDocument, e.target, true); });
+ outerhbox.appendChild(image);
+
+ //richlistitem
+ let richlistitem = aDocument.createElement("richlistitem");
+ richlistitem.appendChild(outerhbox);
+
+ return richlistitem;
+ },
+
+ updatePhoneNumbers: function(aDocument) {
+ let list = aDocument.getElementById("X-DAV-PhoneNumberList");
+
+ let phones = [];
+ for (let i=0; i < list.children.length; i++) {
+ let item = list.children[i];
+ let phone = dav.tools.getPhoneListItemElement(item, "phone").value.trim();
+ if (phone != "") {
+ let json = {};
+ json.meta = dav.tools.getPhoneListItemElement(item, "dataContainer").meta;
+ json.value = phone;
+ phones.push(json);
+ }
+ }
+ aDocument.getElementById("X-DAV-JSON-Phones").value = JSON.stringify(phones);
+
+ //now update all other TB number fields based on the new JSON data
+ let phoneData = dav.tools.getPhoneNumbersFromJSON(aDocument.getElementById("X-DAV-JSON-Phones").value);
+ for (let field in phoneData) {
+ if (phoneData.hasOwnProperty(field)) {
+ aDocument.getElementById(field).value = phoneData[field].join(", ");
+ }
+ }
+ },
+
+ addPhoneEntry: function(aDocument) {
+ let list = aDocument.getElementById("X-DAV-PhoneNumberList");
+ let data = {value: "", meta: ["HOME", "VOICE"]};
+ let item = dav.tools.getNewPhoneListItem(aDocument, data);
+ list.ensureElementIsVisible(list.appendChild(item));
+
+ dav.tools.updateType(aDocument, dav.tools.getPhoneListItemElement(item, "button1"));
+ dav.tools.updateType(aDocument, dav.tools.getPhoneListItemElement(item, "button2"));
+ dav.tools.updatePref(aDocument, dav.tools.getPhoneListItemElement(item, "pref"));
+ },
+
+ getPhoneListItemElement: function(item, element) {
+ switch (element) {
+ case "dataContainer":
+ return item.children[0];
+ case "button1":
+ return item.children[0].children[0];
+ case "button2":
+ return item.children[0].children[1];
+ case "phone":
+ return item.children[0].children[2].children[0];
+ case "pref":
+ return item.children[0].children[3];
+ default:
+ return null;
+ }
+ },
+
+
+
+ //* * * * * * * * * * * * *
+ //* UTILS
+ //* * * * * * * * * * * * *
+
/**
* Convert a byte array to a string - copied from lightning
*
@@ -26,6 +509,7 @@
resultConverter.charset = aCharset || "UTF-8";
return resultConverter.convertFromByteArray(aResult, aResultLength);
} catch (e) {
+ Components.utils.reportError(e);
if (aThrow) {
throw e;
}
@@ -131,11 +615,17 @@
//* SERVER COMMUNICATIONS *
//* * * * * * * * * * * * *
+ addAccountDataToConnectionData: function (connection) {
+ let account = tbSync.db.getAccount(connection.account);
+ connection.https = account.https;
+ connection.user = account.user;
+ connection.password = tbSync.dav.getPassword(account);
+ },
+
Prompt: class {
- constructor(aAccount, aType) {
+ constructor(aConnection) {
this.mCounts = 0;
- this.mAccount = aAccount;
- this.mType = aType;
+ this.mConnection = aConnection;
}
// boolean promptAuth(in nsIChannel aChannel,
@@ -143,48 +633,58 @@
// in nsIAuthInformation authInfo)
promptAuth (aChannel, aLevel, aAuthInfo) {
//store aAuthInfo.realm, needed later to setup lightning passwords
- if (this.mType == "cal") {
+ if (this.mConnection.type == "cal") {
tbSync.dump("Found CalDAV authRealm for <"+aChannel.URI.host+">", aAuthInfo.realm);
dav.listOfRealms[aChannel.URI.host] = aAuthInfo.realm;
}
- //get the password for this account from password manager
- let password = tbSync.dav.getPassword(this.mAccount);
- if (password !== null) {
- tbSync.dump("SUCCEEDED to fetch password from password manager", this.mAccount.user + " @ " + this.mAccount.host);
- aAuthInfo.username = this.mAccount.user;
- aAuthInfo.password = password;
+ if (this.mConnection.password !== null) {
+ aAuthInfo.username = this.mConnection.user;
+ aAuthInfo.password = this.mConnection.password;
} else {
- tbSync.dump("FAILED to fetch password from password manager", this.mAccount.user + " @ " + this.mAccount.host);
+ //we have no password, request one by throwing a 401
+ return false;
}
+ //even if we have a password, it could be wrong, in which case we would be here more than once
this.mCounts++
- if (this.mCounts < 2 && password !== null) {
- return true;
- } else {
- return false; //if the credentials in the password manager are wrong or not found, abort and pass on the 401 to the caller
- }
+ return (this.mCounts < 2);
}
},
Redirect: class {
- constructor() {
+ constructor(aHeaders) {
+ this.mHeaders = aHeaders;
}
asyncOnChannelRedirect (aOldChannel, aNewChannel, aFlags, aCallback) {
- //disallow redirects, we catch the new url and re-initiate the request
- //aNewChannel.cancel(Components.results.NS_BINDING_ABORTED);
- //aOldChannel.cancel(Components.results.NS_BINDING_ABORTED);
- aCallback.onRedirectVerifyCallback(Components.results.NS_ERROR_FAILURE);
+ // Make sure we can get/set headers on both channels.
+ aNewChannel.QueryInterface(Components.interfaces.nsIHttpChannel);
+ aOldChannel.QueryInterface(Components.interfaces.nsIHttpChannel);
+
+ //re-add all headers
+ for (let header in this.mHeaders) {
+ if (this.mHeaders.hasOwnProperty(header)) {
+ aNewChannel.setRequestHeader(header, this.mHeaders[header], false);
+ }
+ }
+
+ //if a user:pass info has been lost, re-add and redirect
+ if (aOldChannel.URI.userPass != "" && aNewChannel.URI.userPass == "") {
+ let uri = Services.io.newURI(aNewChannel.URI.spec.replace("://","://" + aOldChannel.URI.userPass + "@"));
+ aNewChannel.redirectTo(uri);
+ }
+
+ aCallback.onRedirectVerifyCallback(Components.results.NS_OK);
}
},
- prepHttpChannel: function(aUri, aUploadData, aHeaders, aMethod, aAccount, aNotificationCallbacks=null, aExisting=null) {
+ prepHttpChannel: function(aUploadData, aHeaders, aMethod, aConnection, aNotificationCallbacks=null, aExisting=null) {
let channel = aExisting || Services.io.newChannelFromURI2(
- aUri,
+ aConnection.uri,
null,
Services.scriptSecurityManager.getSystemPrincipal(),
- //Services.scriptSecurityManager.createCodebasePrincipal(aUri, {user: aAccount.user}),
+ //Services.scriptSecurityManager.createCodebasePrincipal(aConnection.uri, {user: aConnection.user}),
null,
Components.interfaces.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
Components.interfaces.nsIContentPolicy.TYPE_OTHER);
@@ -240,35 +740,48 @@
return httpchannel;
},
- sendRequest: Task.async (function* (requestData, _url, method, syncdata, headers = {}, options = {softfail: []}, aUseStreamLoader = true) {
- let account = tbSync.db.getAccount(syncdata.account);
-
- //do not modify parameter
- let url = _url;
+ /* connection data:
+ connection.type
+ connection.fqdn
+ connection.https
+ connection.user
+ connection.password
+ [connection.timeout]
+ */
+ sendRequest: Task.async (function* (requestData, path, method, connection, headers = {}, options = {softfail: []}, aUseStreamLoader = true) {
+ //path could be absolute or relative, we may need to rebuild the full url
+ let url = (path.startsWith("http://") || path.startsWith("https://")) ? path : "http" + (connection.https == "1" ? "s" : "") + "://" + connection.fqdn + path;
+
+ //a few bugs in TB and in client implementations require to retry a connection on certain failures
+ for (let i=1; i < 5; i++) { //max number of retries
+ tbSync.dump("URL Request #" + i, url);
+
+ //inject user + password if requested
+ if (tbSync.dav.prefSettings.getBoolPref("addCredentialsToUrl")) {
+ url = url.replace("://","://" + encodeURIComponent(connection.user) + ":" + encodeURIComponent(connection.password) + "@");
+ }
- //manually handling redirects by re-issuing the request to the new url
- for (let i=1; i < 10; i++) { //max number of redirects
- //if the new url is relative, add last known fqdn
- let uri = Services.io.newURI((url.startsWith("http://") || url.startsWith("https://")) ? url : "http" + (account.https == "1" ? "s" : "") + "://" + syncdata.fqdn + url);
- tbSync.dump("URL Request #" + i, uri.spec);
+ connection.uri = Services.io.newURI(url);
//https://bugzilla.mozilla.org/show_bug.cgi?id=669675
- if (dav.problematicHosts.includes(uri.host)) {
- headers["Authorization"] = "Basic " + tbSync.b64encode(account.user + ":" + tbSync.dav.getPassword(account));
+ if (dav.problematicHosts.includes(connection.uri.host)) {
+ headers["Authorization"] = "Basic " + tbSync.b64encode(connection.user + ":" + connection.password);
}
- let r = yield dav.tools.sendRequestCore (requestData, uri.spec, method, syncdata, headers, options, aUseStreamLoader);
- if (r && r.redirect && r.url) {
- url = r.url;
- tbSync.dump("Redirect #" + i, r.url);
- } else if (r && r.retry && r.retry === true) {
+ let r = yield dav.tools.sendRequestCore(requestData, method, connection, headers, options, aUseStreamLoader);
+
+ //connection.uri.host may no longer be the correct value, as there might have been redirects, use connection.fqdn
+ if (r && r.retry && r.retry === true) {
if (r.addBasicAuthHeaderOnce) {
- tbSync.dump("DAV:unauthenticated", "Manually adding basic auth header for <" + account.user + "@" + uri.host + ">");
- headers["Authorization"] = "Basic " + tbSync.b64encode(account.user + ":" + tbSync.dav.getPassword(account));
- } else if (!dav.problematicHosts.includes(uri.host) ) {
- tbSync.dump("BUG 669675", "Adding <" + uri.host + "> to list of problematic hosts.");
- dav.problematicHosts.push(uri.host)
+ tbSync.dump("DAV:unauthenticated", "Manually adding basic auth header for <" + connection.user + "@" + connection.fqdn + ">");
+ headers["Authorization"] = "Basic " + tbSync.b64encode(connection.user + ":" + connection.password);
+ } else if (!dav.problematicHosts.includes(connection.fqdn) ) {
+ tbSync.dump("BUG 669675", "Adding <" + connection.fqdn + "> to list of problematic hosts.");
+ dav.problematicHosts.push(connection.fqdn)
}
+
+ //there might have been a redirect, rebuild url
+ url = "http" + (connection.https == "1" ? "s" : "") + "://" + connection.fqdn + r.path;
} else {
return r;
}
@@ -276,59 +789,104 @@
}),
// Promisified implementation of Components.interfaces.nsIHttpChannel
- sendRequestCore: Task.async (function* (requestData, fullUrl, method, syncdata, headers, options, aUseStreamLoader) {
- let account = tbSync.db.getAccount(syncdata.account);
+ sendRequestCore: Task.async (function* (requestData, method, connection, headers, options, aUseStreamLoader) {
let responseData = "";
- //Note:
- // - by specifying a user, the system falls back to user:, which will trigger a 401 which will cause the authCallbacks and lets me set a new user/pass combination
- // - after it has recevied a new pass, it will use the cached version
- // - this allows to switch users but will cause a 401 on each user switch, and it probably breaks digest auth
- // - the username is lost during redirects...
-
- let finalUrl = fullUrl;
- if (tbSync.dav.prefSettings.getBoolPref("addCredentialsToUrl")) {
- //inject user + password to be used with LOAD_EXPLICIT_CREDENTIALS (does not help with cookie cache)
- finalUrl = fullUrl.replace("://","://" + encodeURIComponent(account.user) + ":" + encodeURIComponent(tbSync.dav.getPassword(account)) + "@");
- }
- let uri = Services.io.newURI(finalUrl);
-
- //no longer log HEADERS, as it could contain an Authorization header
+ //do not log HEADERS, as it could contain an Authorization header
//tbSync.dump("HEADERS", JSON.stringify(headers));
if (tbSync.prefSettings.getIntPref("log.userdatalevel")>1) tbSync.dump("REQUEST", method + " : " + requestData);
+ //testing with fetch()
+/* if (!aUseStreamLoader) {
+ let fetchoptions = {};
+ fetchoptions.method = method;
+ fetchoptions.body = requestData;
+ fetchoptions.cache = "no-cache";
+ //do not include credentials, so we do not end up in a session, see https://github.com/owncloud/core/issues/27093
+ fetchoptions.credentials = "omit";
+ fetchoptions.redirect = "follow";// manual, *follow, error
+ fetchoptions.headers = headers;
+ fetchoptions.headers["Content-Length"] = requestData.length;
+
+ if (!fetchoptions.headers.hasOwnProperty("Content-Type"))
+ fetchoptions.headers["Content-Type"] = "application/xml; charset=utf-8";
+
+ fetchoptions.headers["Authorization"] = "Basic " + btoa(connection.user + ':' + connection.password);
+ tbSync.dump("FETCH URL", connection.uri.spec);
+ tbSync.dump("FETCH OPTIONS", JSON.stringify(fetchoptions));
+
+ try {
+ let response = yield tbSync.window.fetch(connection.uri.spec, fetchoptions);
+ tbSync.dump("FETCH STATUS", response.status);
+ let text = yield response.text();
+ tbSync.dump("FETCH RESPONSE", response.status + " : " + text);
+ } catch (e) {
+ Components.utils.reportError(e);
+ tbSync.dump("FETCH FAILED", "");
+ }
+ return null;
+ }*/
+
return new Promise(function(resolve, reject) {
let listener = {
+ _data: "",
+ _stream: null,
+
//nsIStreamListener (aUseStreamLoader = false)
- onStopRequest: function(aRequest, aContext, aStatusCode) {
- Services.console.logStringMessage("[onStopRequest] " + aStatusCode);
- },
onStartRequest: function(aRequest, aContext) {
Services.console.logStringMessage("[onStartRequest] ");
+ this.data = "";
},
onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) {
Services.console.logStringMessage("[onDataAvailable] " + aCount);
+ if (this._stream == null) {
+ this._stream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
+ this._stream.init(aInputStream);
+ }
+ let d = this._stream.read(aCount);
+ //tbSync.dump("STREAM", d);
+ this._data += d;
+ },
+ onStopRequest: function(aRequest, aContext, aStatusCode) {
+ Services.console.logStringMessage("[onStopRequest] " + aStatusCode);
+ this.processResponse(aRequest.QueryInterface(Components.interfaces.nsIHttpChannel), aContext, aStatusCode, this._data);
},
- //nsIStreamLoaderObserve (aUseStreamLoader = true)
+ //nsIStreamLoaderObserver (aUseStreamLoader = true)
onStreamComplete: function(aLoader, aContext, aStatus, aResultLength, aResult) {
- let request = aLoader.request.QueryInterface(Components.interfaces.nsIHttpChannel);
+ let result = dav.tools.convertByteArray(aResult, aResultLength);
+ this.processResponse(aLoader.request.QueryInterface(Components.interfaces.nsIHttpChannel), aContext, aStatus, result);
+ },
+
+ processResponse: function(aChannel, aContext, aStatus, aResult) {
let responseStatus = 0;
try {
- responseStatus = request.responseStatus;
+ responseStatus = aChannel.responseStatus;
} catch (ex) {
- let error = tbSync.createTCPErrorFromFailedChannel(aLoader.request);
+ let error = tbSync.createTCPErrorFromFailedChannel(aChannel);
if (!error) {
- return reject(dav.sync.failed("networkerror", "URL:\n" + fullUrl + " ("+method+")")); //reject/resolve do not terminate control flow
+ return reject(dav.sync.failed("networkerror", "URL:\n" + connection.uri.spec + " ("+method+")")); //reject/resolve do not terminate control flow
} else {
- return reject(dav.sync.failed(error, "URL:\n" + fullUrl + " ("+method+")"));
+ return reject(dav.sync.failed(error, "URL:\n" + connection.uri.spec + " ("+method+")"));
}
}
- let text = dav.tools.convertByteArray(aResult, aResultLength);
- if (tbSync.prefSettings.getIntPref("log.userdatalevel")>1) tbSync.dump("RESPONSE", responseStatus + " : " + text);
- responseData = text.split("><").join(">\n<");
+ if (tbSync.prefSettings.getIntPref("log.userdatalevel")>1) tbSync.dump("RESPONSE", responseStatus + " ("+aChannel.responseStatusText+")" + " : " + aResult);
+ responseData = aResult.split("><").join(">\n<");
+ //Redirected? Update connection settings from current URL
+ if (aChannel.URI) {
+ let newHttps = (aChannel.URI.scheme == "https") ? "1" : "0";
+ if (connection.https != newHttps) {
+ tbSync.dump("Updating HTTPS", connection.https + " -> " + newHttps);
+ connection.https = newHttps;
+ }
+ if (connection.fqdn != aChannel.URI.hostPort) {
+ tbSync.dump("Updating FQDN", connection.fqdn + " -> " + aChannel.URI.hostPort);
+ connection.fqdn = aChannel.URI.hostPort;
+ }
+ }
+
switch(responseStatus) {
case 301:
case 302:
@@ -337,9 +895,11 @@
case 307:
case 308:
{
+ //Since we now use the nsIChannelEventSink to handle the redirects, this should never be called.
+ //Just in case, do a retry with the updated connection settings.
let response = {};
- response.redirect = responseStatus;
- response.url = request.getResponseHeader("Location");
+ response.retry = true;
+ response.path = aChannel.URI.pathQueryRef;
return resolve(response);
}
break;
@@ -355,11 +915,11 @@
//I hope this bug gets fixed soon
//should the channel have been able to authenticate (password is stored)?
- if (tbSync.dav.getPassword(account) !== null) {
+ if (connection.password !== null) {
//did the channel try to authenticate?
let triedToAuthenticate;
try {
- let header = request.getRequestHeader("Authorization");
+ let header = aChannel.getRequestHeader("Authorization");
triedToAuthenticate = true;
} catch (e) {
triedToAuthenticate = false;
@@ -368,23 +928,25 @@
if (!triedToAuthenticate) {
let response = {};
response.retry = true;
+ response.path = aChannel.URI.pathQueryRef;
return resolve(response);
}
}
- return reject(dav.sync.failed("401"));
+ return reject(dav.sync.failed(responseStatus, "URL:\n" + connection.uri.spec + " ("+method+")" + "\n\nRequest:\n" + requestData + "\n\nResponse:\n" + responseData));
}
break;
case 207: //preprocess multiresponse
{
- let xml = dav.tools.convertToXML(text);
- if (xml === null) return reject(dav.sync.failed("maiformed-xml", "URL:\n" + fullUrl + " ("+method+")" + "\n\nRequest:\n" + requestData + "\n\nResponse:\n" + responseData));
+ let xml = dav.tools.convertToXML(aResult);
+ if (xml === null) return reject(dav.sync.failed("maiformed-xml", "URL:\n" + connection.uri.spec + " ("+method+")" + "\n\nRequest:\n" + requestData + "\n\nResponse:\n" + responseData));
//the specs allow to return a 207 with DAV:unauthenticated if not authenticated
if (xml.documentElement.getElementsByTagNameNS(dav.ns.d, "unauthenticated").length != 0) {
let response = {};
response.retry = true;
+ response.path = aChannel.URI.pathQueryRef;
//we have no information at all about allowed auth methods, try basic auth
response.addBasicAuthHeaderOnce = true;
return resolve(response);
@@ -396,6 +958,7 @@
response.multi = [];
for (let i=0; i < multi.length; i++) {
let hrefNode = dav.tools.evaluateNode(multi[i], [["d","href"]]);
+ let responseStatusNode = dav.tools.evaluateNode(multi[i], [["d", "status"]]);
let propstats = multi[i].getElementsByTagNameNS(dav.ns.d, "propstat");
if (propstats.length > 0) {
//response contains propstats, push each as single entry
@@ -405,16 +968,16 @@
let resp = {};
resp.node = propstats[p];
resp.status = statusNode === null ? null : statusNode.textContent.split(" ")[1];
+ resp.responsestatus = responseStatusNode === null ? null : responseStatusNode.textContent.split(" ")[1];
resp.href = hrefNode === null ? null : hrefNode.textContent;
response.multi.push(resp);
}
} else {
//response does not contain any propstats, push raw response
- let statusNode = dav.tools.evaluateNode(multi[i], [["d", "status"]]);
-
let resp = {};
resp.node = multi[i];
- resp.status = statusNode === null ? null : statusNode.textContent.split(" ")[1];
+ resp.status = responseStatusNode === null ? null : responseStatusNode.textContent.split(" ")[1];
+ resp.responsestatus = responseStatusNode === null ? null : responseStatusNode.textContent.split(" ")[1];
resp.href = hrefNode === null ? null : hrefNode.textContent;
response.multi.push(resp);
}
@@ -435,7 +998,7 @@
if (options.softfail.includes(responseStatus)) {
let noresponse = {};
noresponse.softerror = responseStatus;
- let xml = dav.tools.convertToXML(text);
+ let xml = dav.tools.convertToXML(aResult);
if (xml !== null) {
let exceptionNode = dav.tools.evaluateNode(xml.documentElement, [["s","exception"]]);
if (exceptionNode !== null) {
@@ -443,10 +1006,10 @@
}
}
//manually log this non-fatal error
- tbSync.errorlog("info", syncdata, "softerror::"+responseStatus, "URL:\n" + fullUrl + " ("+method+")" + "\n\nRequest:\n" + requestData + "\n\nResponse:\n" + responseData);
+ tbSync.errorlog("info", connection, "softerror::"+responseStatus, "URL:\n" + connection.uri.spec + " ("+method+")" + "\n\nRequest:\n" + requestData + "\n\nResponse:\n" + responseData);
return resolve(noresponse);
} else {
- return reject(dav.sync.failed(responseStatus, "URL:\n" + fullUrl + " ("+method+")" + "\n\nRequest:\n" + requestData + "\n\nResponse:\n" + responseData));
+ return reject(dav.sync.failed(responseStatus, "URL:\n" + connection.uri.spec + " ("+method+")" + "\n\nRequest:\n" + requestData + "\n\nResponse:\n" + responseData));
}
break;
@@ -460,7 +1023,7 @@
if (aIID.equals(Components.interfaces.nsIAuthPrompt2)) {
tbSync.dump("GET","nsIAuthPrompt2");
if (!this.authPrompt) {
- this.authPrompt = new dav.tools.Prompt(account, syncdata.type);
+ this.authPrompt = new dav.tools.Prompt(connection);
}
return this.authPrompt;
} else if (aIID.equals(Components.interfaces.nsIAuthPrompt)) {
@@ -473,7 +1036,7 @@
//tbSync.dump("GET","nsIProgressEventSink");
} else if (aIID.equals(Components.interfaces.nsIChannelEventSink)) {
if (!this.redirectSink) {
- this.redirectSink = new dav.tools.Redirect();
+ this.redirectSink = new dav.tools.Redirect(headers);
}
return this.redirectSink;
}
@@ -482,7 +1045,7 @@
},
}
- let channel = dav.tools.prepHttpChannel(uri, requestData, headers, method, account, notificationCallbacks);
+ let channel = dav.tools.prepHttpChannel(requestData, headers, method, connection, notificationCallbacks);
if (aUseStreamLoader) {
let loader = Components.classes["@mozilla.org/network/stream-loader;1"].createInstance(Components.interfaces.nsIStreamLoader);
loader.init(listener);
@@ -490,15 +1053,15 @@
}
//manually set timout
- syncdata.timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
- let timeout = tbSync.prefSettings.getIntPref("timeout");
+ connection.timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
+ let timeout = connection.hasOwnProperty("timeout") ? connection.timeout : tbSync.prefSettings.getIntPref("timeout");
let rv = Components.results.NS_ERROR_NET_TIMEOUT;
let event = {
notify: function(timer) {
if (channel) channel.cancel(rv);
}
}
- syncdata.timer.initWithCallback(event, timeout, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+ connection.timer.initWithCallback(event, timeout, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
channel.asyncOpen(listener, channel);
@@ -727,23 +1290,24 @@
//* ACTUAL SYNC MAGIC *
//* * * * * * * * * * *
- //helper function: check vCardData object if it has a meta.type associated
- itemHasMetaType: function (vCardData, item, entry, typefield) {
- return (vCardData[item][entry].meta &&
- vCardData[item][entry].meta[typefield] &&
- vCardData[item][entry].meta[typefield].length > 0);
+ //helper function: extract the associated meta.type of an entry
+ getItemMetaType: function (vCardData, item, i, typefield) {
+ let arr = [];
+ if (vCardData[item][i].meta && vCardData[item][i].meta[typefield] && vCardData[item][i].meta[typefield].length > 0) {
+ //bug in vCard parser? type is always array of length 1, all values joined by ,
+ //no, sometimes it is a true array
+ for (let d=0; d < vCardData[item][i].meta[typefield].length; d++) {
+ arr = arr.concat( vCardData[item][i].meta[typefield][d].split(",").map(function(x){ return x.toUpperCase().trim() }) );
+ }
+ }
+ return arr;
},
//helper function: for each entry for the given item, extract the associated meta.type
getMetaTypeData: function (vCardData, item, typefield) {
let metaTypeData = [];
for (let i=0; i < vCardData[item].length; i++) {
- if (dav.tools.itemHasMetaType(vCardData, item, i, typefield)) {
- //bug in vCard parser? type is always array of length 1, all values joined by ,
- metaTypeData.push( vCardData[item][i].meta[typefield][0].split(",").map(function(x){ return x.toUpperCase().trim() }) );
- } else {
- metaTypeData.push([]);
- }
+ metaTypeData.push( dav.tools.getItemMetaType(vCardData, item, i, typefield) );
}
return metaTypeData;
},
@@ -763,16 +1327,17 @@
} else if (index == 0) return vCardValue;
else return "";
},
-
+
supportedProperties: [
{name: "DisplayName", minversion: "0.4"},
{name: "FirstName", minversion: "0.4"},
+ {name: "X-DAV-PrefixName", minversion: "0.12.13"},
{name: "X-DAV-MiddleName", minversion: "0.8.8"},
- {name: "X-DAV-MainPhone", minversion: "0.8.8"},
+ {name: "X-DAV-SuffixName", minversion: "0.12.13"},
{name: "X-DAV-UID", minversion: "0.10.36"},
+ {name: "X-DAV-JSON-Phones", minversion: "0.4"},
+ {name: "X-DAV-JSON-Emails", minversion: "0.4"},
{name: "LastName", minversion: "0.4"},
- {name: "PrimaryEmail", minversion: "0.4"},
- {name: "SecondEmail", minversion: "0.4"},
{name: "NickName", minversion: "0.4"},
{name: "Birthday", minversion: "0.4"}, //fake, will trigger special handling
{name: "Photo", minversion: "0.4"}, //fake, will trigger special handling
@@ -781,22 +1346,17 @@
{name: "HomeZipCode", minversion: "0.4"},
{name: "HomeState", minversion: "0.4"},
{name: "HomeAddress", minversion: "0.4"},
- {name: "HomePhone", minversion: "0.4"},
{name: "WorkCity", minversion: "0.4"},
{name: "WorkCountry", minversion: "0.4"},
{name: "WorkZipCode", minversion: "0.4"},
{name: "WorkState", minversion: "0.4"},
{name: "WorkAddress", minversion: "0.4"},
- {name: "WorkPhone", minversion: "0.4"},
{name: "Categories", minversion: "0.4"},
{name: "JobTitle", minversion: "0.4"},
{name: "Department", minversion: "0.4"},
{name: "Company", minversion: "0.4"},
{name: "WebPage1", minversion: "0.4"},
{name: "WebPage2", minversion: "0.4"},
- {name: "CellularNumber", minversion: "0.4"},
- {name: "PagerNumber", minversion: "0.4"},
- {name: "FaxNumber", minversion: "0.4"},
{name: "Notes", minversion: "0.4"},
{name: "PreferMailFormat", minversion: "0.4"},
{name: "Custom1", minversion: "0.4"},
@@ -827,7 +1387,9 @@
"Categories" : "categories",
"Notes" : "note",
"FirstName" : "n",
+ "X-DAV-PrefixName" : "n",
"X-DAV-MiddleName" : "n",
+ "X-DAV-SuffixName" : "n",
"LastName" : "n",
"PreferMailFormat" : "X-MOZILLA-HTML",
"Custom1" : "X-MOZILLA-CUSTOM1",
@@ -840,23 +1402,18 @@
complexMap : {
"WebPage1" : {item: "url", type: "WORK"},
"WebPage2" : {item: "url", type: "HOME"},
- "CellularNumber" : {item: "tel", type: "CELL"},
- "PagerNumber" : {item: "tel", type: "PAGER"},
- "FaxNumber" : {item: "tel", type: "FAX"},
"HomeCity" : {item: "adr", type: "HOME"},
"HomeCountry" : {item: "adr", type: "HOME"},
"HomeZipCode" : {item: "adr", type: "HOME"},
"HomeState" : {item: "adr", type: "HOME"},
"HomeAddress" : {item: "adr", type: "HOME"},
- "HomePhone" : {item: "tel", type: "HOME", invalidTypes: ["FAX", "PAGER", "CELL"]},
"WorkCity" : {item: "adr", type: "WORK"},
"WorkCountry" : {item: "adr", type: "WORK"},
"WorkZipCode" : {item: "adr", type: "WORK"},
"WorkState" : {item: "adr", type: "WORK"},
"WorkAddress" : {item: "adr", type: "WORK"},
- "WorkPhone" : {item: "tel", type: "WORK", invalidTypes: ["FAX", "PAGER", "CELL"]},
},
//map thunderbird fields to impp vcard fields with additional x-service-types
@@ -884,95 +1441,33 @@
let data = {item: "", metatype: [], metatypefield: "type", entry: -1, prefix: ""};
if (vCardData) {
+
//handle special cases independently, those from *Map will be handled by default
switch (property) {
- case "PrimaryEmail":
- case "SecondEmail":
- {
- //assign email types based on emailSwapInvert
- let emailMap = {"PrimaryEmail.0": "PrimaryEmail", "SecondEmail.0": "SecondEmail", "PrimaryEmail.1": "SecondEmail", "SecondEmail.1": "PrimaryEmail"};
- let emailSwap = syncdata.emailSwapInvert == "1" ? "1" : "0";
- let emailType = emailMap[property + "." + emailSwap];
-
- let metamap = (tbSync.db.getAccountSetting(syncdata.account, "useHomeAsPrimary") == "0") ? {"PrimaryEmail": "WORK", "SecondEmail": "HOME"} : {"PrimaryEmail": "HOME", "SecondEmail": "WORK"};
- data.metatype.push(metamap[emailType]);
- data.item = "email";
-
- //search the first valid entry
- if (vCardData[data.item]) {
- let metaTypeData = dav.tools.getMetaTypeData(vCardData, data.item, data.metatypefield);
-
- //check metaTypeData to find correct entry
- if (emailType == "PrimaryEmail") {
-
- let prev = [];
- let primary = [];
- let primaryPrev = [];
- let otherButNotSecondary = [];
- for (let i=0; i < metaTypeData.length; i++) {
- if (metaTypeData[i].includes(metamap.PrimaryEmail) && metaTypeData[i].includes("PREV")) primaryPrev.push(i);
- if (metaTypeData[i].includes("PREV") && !metaTypeData[i].includes(metamap.SecondEmail)) prev.push(i);
- if (metaTypeData[i].includes(metamap.PrimaryEmail)) primary.push(i);
- if (!metaTypeData[i].includes(metamap.SecondEmail) && !metaTypeData[i].includes("INTERNET")) otherButNotSecondary.push(i);
- }
- if (primaryPrev.length > 0) data.entry = primaryPrev[0];
- else if (prev.length > 0) data.entry = prev[0];
- else if (primary.length > 0) data.entry = primary[0];
- else if (otherButNotSecondary.length > 0) data.entry = otherButNotSecondary[0];
-
- } else {
-
- let secondaryPrev = [];
- let secondary = [];
- let internet = [];
- for (let i=0; i < metaTypeData.length; i++) {
- if (metaTypeData[i].includes(metamap.SecondEmail) && metaTypeData[i].includes("PREV")) secondaryPrev.push(i);
- if (metaTypeData[i].includes(metamap.SecondEmail)) secondary.push(i);
- if (metaTypeData[i].includes("INTERNET")) internet.push(i);
- }
- if (secondaryPrev.length > 0) data.entry = secondaryPrev[0];
- else if (secondary.length > 0) data.entry = secondary[0];
- else if (internet.length > 0) data.entry = internet[0];
-
- }
- }
+ case "X-DAV-JSON-Emails":
+ {
+ data.metatype.push("OTHER"); //default for new entries
+ data.item = "email";
+
+ if (vCardData[data.item] && vCardData[data.item].length > 0) {
+ //NOOP, just return something, if present
+ data.entry = 0;
}
- break;
-
- case "X-DAV-MainPhone":
- {
- data.metatype.push("PREV");
- data.item = "tel";
-
- //search the first valid entry
- if (vCardData[data.item]) {
- let metaTypeData = dav.tools.getMetaTypeData(vCardData, data.item, data.metatypefield);
-
- //we take everything that is not HOME, WORK, CELL, PAGER or FAX
- //we take PREV over MAIN (fruux) over VOICE over ?
- let tel = {};
- tel.prev =[];
- tel.main =[];
- tel.voice =[];
- tel.other =[];
- for (let i=0; i < metaTypeData.length; i++) {
- if (!metaTypeData[i].includes("HOME") && !metaTypeData[i].includes("WORK") && !metaTypeData[i].includes("CELL") && !metaTypeData[i].includes("PAGER") && !metaTypeData[i].includes("FAX")) {
- if (metaTypeData[i].includes("PREV")) tel.prev.push(i);
- else if (metaTypeData[i].includes("MAIN")) tel.main.push(i);
- else if (metaTypeData[i].includes("VOICE")) tel.voice.push(i);
- else tel.other.push(i);
- }
- }
-
- if (tel.prev.length > 0) data.entry = tel.prev[0];
- else if (tel.main.length > 0) data.entry = tel.main[0];
- else if (tel.voice.length > 0) data.entry = tel.voice[0];
- else if (tel.other.length > 0) data.entry = tel.other[0];
+ }
+ break;
- }
+ case "X-DAV-JSON-Phones":
+ {
+ data.metatype.push("VOICE"); //default for new entries
+ data.item = "tel";
+
+ if (vCardData[data.item] && vCardData[data.item].length > 0) {
+ //NOOP, just return something, if present
+ data.entry = 0;
}
- break;
-
+ }
+ break;
+
default:
//Check *Maps
if (dav.tools.simpleMap.hasOwnProperty(property)) {
@@ -1018,7 +1513,6 @@
} else throw "Unknown TB property <"+property+">";
}
}
-
return data;
},
@@ -1064,9 +1558,11 @@
case "FirstName":
case "LastName":
+ case "X-DAV-PrefixName":
case "X-DAV-MiddleName":
+ case "X-DAV-SuffixName":
{
- let index = ["LastName","FirstName","X-DAV-MiddleName","Prefix","Suffix"].indexOf(property);
+ let index = ["LastName","FirstName","X-DAV-MiddleName","X-DAV-PrefixName","X-DAV-SuffixName"].indexOf(property);
return dav.tools.getSaveArrayValue(vCardValue, index);
}
break;
@@ -1089,10 +1585,29 @@
return 0;
break;
- default: //should be a single string
- let v = (Array.isArray(vCardValue)) ? vCardValue.join(" ") : vCardValue;
- if (vCardField.prefix.length > 0 && v.startsWith(vCardField.prefix)) return v.substring(vCardField.prefix.length);
- else return v;
+ case "X-DAV-JSON-Phones":
+ case "X-DAV-JSON-Emails":
+ {
+ //this is special, we need to return the full JSON object
+ let entries = [];
+ let metaTypeData = dav.tools.getMetaTypeData(vCardData, vCardField.item, vCardField.metatypefield);
+ for (let i=0; i < metaTypeData.length; i++) {
+ let entry = {};
+ entry.meta = metaTypeData[i];
+ entry.value = vCardData[vCardField.item][i].value;
+ entries.push(entry);
+ }
+ return JSON.stringify(entries);
+ }
+ break;
+
+ default:
+ {
+ //should be a single string
+ let v = (Array.isArray(vCardValue)) ? vCardValue.join(" ") : vCardValue;
+ if (vCardField.prefix.length > 0 && v.startsWith(vCardField.prefix)) return v.substring(vCardField.prefix.length);
+ else return v;
+ }
}
},
@@ -1151,10 +1666,12 @@
break;
case "FirstName":
+ case "X-DAV-PrefixName":
case "X-DAV-MiddleName":
+ case "X-DAV-SuffixName":
case "LastName":
{
- let index = ["LastName","FirstName","X-DAV-MiddleName","Prefix","Suffix"].indexOf(property);
+ let index = ["LastName","FirstName","X-DAV-MiddleName","X-DAV-PrefixName","X-DAV-SuffixName"].indexOf(property);
if (store) {
if (add) vCardData[vCardField.item][vCardField.entry].value = ["","","","",""];
@@ -1197,6 +1714,17 @@
}
break;
+ case "Emails": //also update meta
+ case "Phones": //also update meta
+ if (store) {
+ vCardData[vCardField.item][vCardField.entry].value = vCardField.prefix + value;
+ if (!vCardData[vCardField.item][vCardField.entry].hasOwnProperty("meta")) {
+ vCardData[vCardField.item][vCardField.entry].meta = {};
+ }
+ vCardData[vCardField.item][vCardField.entry].meta[vCardField.metatypefield] = vCardField.metatype;
+ } else if (remove) vCardData[vCardField.item][vCardField.entry].value = "";
+ break;
+
default: //should be a string
if (store) vCardData[vCardField.item][vCardField.entry].value = vCardField.prefix + value;
else if (remove) vCardData[vCardField.item][vCardField.entry].value = "";
@@ -1212,11 +1740,7 @@
setThunderbirdCardFromVCard: function(syncdata, addressBook, card, vCardData, oCardData = null) {
if (tbSync.prefSettings.getIntPref("log.userdatalevel")>1) tbSync.dump("JSON from vCard", JSON.stringify(vCardData));
//if (oCardData) tbSync.dump("JSON from oCard", JSON.stringify(oCardData));
-
- //keep track of emails
- syncdata.emailSwapInvert = card.getProperty("X-DAV-SWAP","0");
- let emails = {PrimaryEmail: "", SecondEmail: ""};
-
+
for (let f=0; f < dav.tools.supportedProperties.length; f++) {
//Skip sync fields that have been added after this folder was created (otherwise we would delete them)
if (Services.vc.compare(dav.tools.supportedProperties[f].minversion, syncdata.folderCreatedWithProviderVersion)> 0) continue;
@@ -1227,9 +1751,6 @@
let oCardField = dav.tools.getVCardField(syncdata, property, oCardData);
let oldServerValue = dav.tools.getThunderbirdPropertyValueFromVCard(syncdata, property, oCardData, oCardField);
-
- //keep track of emails
- if (emails.hasOwnProperty(property)) emails[property] = newServerValue;
//smart merge: only update the property, if it has changed on the server (keep local modifications)
if (newServerValue !== oldServerValue) {
@@ -1266,6 +1787,33 @@
}
break;
+ case "X-DAV-JSON-Emails":
+ case "X-DAV-JSON-Phones":
+ {
+ //This field contains all the JSON encoded values and TbSync has its own UI to display them.
+ //However, we also want to fill the standard TB fields.
+ let jsonData;
+ switch (property) {
+ case "X-DAV-JSON-Emails" :
+ jsonData = dav.tools.getEmailsFromJSON(newServerValue);
+ break;
+ case "X-DAV-JSON-Phones" :
+ jsonData = dav.tools.getPhoneNumbersFromJSON(newServerValue);
+ break;
+ }
+
+ for (let field in jsonData) {
+ if (jsonData.hasOwnProperty(field)) {
+ //set or delete TB Property
+ if ( jsonData[field].length > 0 ) {
+ card.setProperty(field, jsonData[field].join(", "));
+ } else {
+ card.deleteProperty(field);
+ }
+ }
+ }
+ }
+
default:
{
if (newServerValue) {
@@ -1283,18 +1831,6 @@
}
}
}
-
- //Special handling of PrimaryEmail
- if (!emails["PrimaryEmail"] && emails["SecondEmail"]) {
- //adjust swap setting
- card.setProperty("X-DAV-SWAP", syncdata.emailSwapInvert == "0" ? "1" : "0");
- //swap
- card.setProperty("PrimaryEmail", emails["SecondEmail"]);
- card.setProperty("SecondEmail", "");
- try {
- card.deleteProperty("SecondEmail");
- } catch (e) {}
- }
},
invalidateThunderbirdCard: function(syncdata, addressBook, id) {
@@ -1360,8 +1896,6 @@
getVCardFromThunderbirdContactCard: function(syncdata, addressBook, card, generateUID = false) {
let currentCard = tbSync.getPropertyOfCard(card, "X-DAV-VCARD").trim();
let vCardData = tbSync.dav.vCard.parse(currentCard);
-
- syncdata.emailSwapInvert = card.getProperty("X-DAV-SWAP","0");
for (let f=0; f < dav.tools.supportedProperties.length; f++) {
//Skip sync fields that have been added after this folder was created (otherwise we would delete them)
@@ -1407,13 +1941,73 @@
}
break;
+ case "X-DAV-JSON-Emails":
+ {
+ //this gets us all emails
+ let emails = dav.tools.getEmailsFromCard(card);
+ let idx = 0;
+
+ //store default meta type
+ let defaultMeta = vCardField.metatype;
+
+ for (let i=0; i < emails.length || (vCardData.hasOwnProperty(vCardField.item) && idx < vCardData[vCardField.item].length); i++) {
+ //get value or or empty if entry is to be deleted
+ let value = (i < emails.length) ? emails[i].value : "";
+
+ //fix for bug 1522453 - ignore these
+ if (value.endsWith("@bug1522453"))
+ continue;
+
+ //do we have a meta type? otherwise stick to default
+ if (i < emails.length && emails[i].meta.length > 0) {
+ vCardField.metatype = emails[i].meta;
+ } else {
+ vCardField.metatype= defaultMeta;
+ }
+
+ //remove: value == "" and index != -1
+ //add value != "" and index == -1
+ vCardField.entry = idx++;
+ if (!(vCardData.hasOwnProperty(vCardField.item) && vCardField.entry < vCardData[vCardField.item].length)) vCardField.entry = -1; //need to add a new one
+
+ dav.tools.updateValueOfVCard(syncdata, "Emails", vCardData, vCardField, value);
+ }
+ }
+ break;
+
+ case "X-DAV-JSON-Phones":
+ {
+ //this gets us all phones
+ let phones = dav.tools.getPhoneNumbersFromCard(card);
+ let idx = 0;
+
+ //store default meta type
+ let defaultMeta = vCardField.metatype;
+
+ for (let i=0; i < phones.length || (vCardData.hasOwnProperty(vCardField.item) && idx < vCardData[vCardField.item].length); i++) {
+ //get value or or empty if entry is to be deleted
+ let value = (i < phones.length) ? phones[i].value : "";
+
+ //do we have a meta type? otherwise stick to default
+ if (i < phones.length && phones[i].meta.length > 0) {
+ vCardField.metatype = phones[i].meta;
+ } else {
+ vCardField.metatype= defaultMeta;
+ }
+
+ //remove: value == "" and index != -1
+ //add value != "" and index == -1
+ vCardField.entry = idx++;
+ if (!(vCardData.hasOwnProperty(vCardField.item) && vCardField.entry < vCardData[vCardField.item].length)) vCardField.entry = -1; //need to add a new one
+
+ dav.tools.updateValueOfVCard(syncdata, "Phones", vCardData, vCardField, value);
+ }
+ }
+ break;
+
default:
{
let value = card.getProperty(property, "");
- //fix for bug 1522453
- if ((property == "PrimaryEmail" || property == "SecondEmail") && value.endsWith("@bug1522453")) {
- value = "";
- }
dav.tools.updateValueOfVCard(syncdata, property, vCardData, vCardField, value);
}
break;
@@ -1422,13 +2016,10 @@
if (generateUID) {
//the UID differs from the href/TBSYNCID (following the specs)
- let uid = card.getProperty("X-DAV-UID", "");
- if (!uid) {
- uid = dav.tools.generateUUID();
- card.setProperty("X-DAV-UID", uid);
- //this card has added_by_user status and thus this mod will not trigger a UI notification
- addressBook.modifyCard(card);
- }
+ let uid = dav.tools.generateUUID();
+ card.setProperty("X-DAV-UID", uid);
+ //this card has added_by_user status and thus this mod will not trigger a UI notification
+ addressBook.modifyCard(card);
vCardData["uid"] = [{"value": uid}];
}
diff -Nru dav4tbsync-0.11/debian/changelog dav4tbsync-0.15/debian/changelog
--- dav4tbsync-0.11/debian/changelog 2019-02-09 09:35:02.000000000 +0000
+++ dav4tbsync-0.15/debian/changelog 2019-02-27 05:44:48.000000000 +0000
@@ -1,3 +1,10 @@
+dav4tbsync (0.15-1) unstable; urgency=medium
+
+ [ Mechtilde ]
+ * New upstream version 0.15
+
+ -- Mechtilde Stehmann Wed, 27 Feb 2019 06:44:48 +0100
+
dav4tbsync (0.11-2) unstable; urgency=medium
[ Mechtilde ]
diff -Nru dav4tbsync-0.11/debian/compat dav4tbsync-0.15/debian/compat
--- dav4tbsync-0.11/debian/compat 2018-12-23 10:39:13.000000000 +0000
+++ dav4tbsync-0.15/debian/compat 2019-02-27 05:37:02.000000000 +0000
@@ -1 +1 @@
-11
+12
diff -Nru dav4tbsync-0.11/debian/control dav4tbsync-0.15/debian/control
--- dav4tbsync-0.11/debian/control 2019-02-09 08:31:13.000000000 +0000
+++ dav4tbsync-0.15/debian/control 2019-02-27 05:38:45.000000000 +0000
@@ -3,7 +3,7 @@
Priority: optional
Maintainer: Debian Mozilla Extension Maintainers
Uploaders: Mechtilde Stehmann
-Build-Depends: debhelper (>= 11), mozilla-devscripts,
+Build-Depends: debhelper (>= 12), mozilla-devscripts,
Standards-Version: 4.3.0
Vcs-Git: https://salsa.debian.org/webext-team/dav4tbsync.git/
Vcs-Browser: https://salsa.debian.org/webext-team/dav4tbsync
@@ -13,9 +13,9 @@
Architecture: all
Depends: ${misc:Depends}
, ${xpi:Depends}
- , thunderbird (>= 52)
+ , thunderbird (>= 60)
, lightning | xul-ext-lightning
- , webext-tbsync
+ , webext-tbsync (>= 1.7)
Recommends: ${xpi:Recommends}
Provides: ${xpi:Provides}
Enhances: ${xpi:Enhances}
diff -Nru dav4tbsync-0.11/install.rdf dav4tbsync-0.15/install.rdf
--- dav4tbsync-0.11/install.rdf 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/install.rdf 2019-02-26 16:26:01.000000000 +0000
@@ -8,7 +8,7 @@
false
true
Provider for CalDAV & CardDAV
- 0.11
+ 0.15
Add sync support for CalDAV & CardDAV accounts to TbSync
John Bieling
https://github.com/jobisoft/DAV-4-TbSync
diff -Nru dav4tbsync-0.11/locale/de/dav.dtd dav4tbsync-0.15/locale/de/dav.dtd
--- dav4tbsync-0.11/locale/de/dav.dtd 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/de/dav.dtd 2019-02-26 16:26:01.000000000 +0000
@@ -6,6 +6,7 @@
+
@@ -31,14 +32,29 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -Nru dav4tbsync-0.11/locale/de/dav.strings dav4tbsync-0.15/locale/de/dav.strings
--- dav4tbsync-0.11/locale/de/dav.strings 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/de/dav.strings 2019-02-26 16:26:01.000000000 +0000
@@ -1,5 +1,8 @@
menu.name = CalDAV & CardDAV
+defaultname.calendar=Kalender
+defaultname.contacts=Kontakte
+
syncstate.syncing=Initiiere Synchronisation
syncstate.preparing=Bereite das nächste Element für die Synchronisation vor
syncstate.done=Bereite das nächste Element für die Synchronisation vor
@@ -26,6 +29,7 @@
status.401=Authentifizierung fehlgeschlagen, überprüfen Sie den Benutzernamen und das Passwort.
status.500=Unbekannter Server Fehler (HTTP Fehler 500).
status.503=Service nicht erreichbar.
+helplink.503=https://github.com/jobisoft/DAV-4-TbSync/issues/41
status.httperror=Kommunikationsfehler (HTTP Status ##replace.1##).
status.OK=OK
status.nolightning=Lightning Add-On nicht installiert, Kalender nicht nutzbar.
@@ -41,8 +45,11 @@
status.OK.caldav-calendar-already-exists=Kalender existiert bereits
status.info.restored=Wegen teilweise fehlender Schreibrechte wurden einige Aktionen vom Server zurückgewiesen und lokal rückgängig gemacht.
status.maiformed-xml=Antwort enthält fehlerhaftes XML, Sync abgebrochen. Prüfen Sie bitte den Fehlerbericht für weitere Details.
+helplink.maiformed-xml=https://github.com/jobisoft/DAV-4-TbSync/issues/59#issuecomment-459685281
status.service-discovery-failed=Der sabre/dav service discovery funktioniert mit diesem Server nicht.
status.missing-permission=Fehlende Berechtigung: ##replace.1##
+status.caldavservernotfound=Es wurde kein CalDAV Server gefunden.
+status.carddavservernotfound=Es wurde kein CardDAV Server gefunden.
status.OK.managedbylightning=Kalender wird von lightning verwaltet.
diff -Nru dav4tbsync-0.11/locale/en-US/dav.dtd dav4tbsync-0.15/locale/en-US/dav.dtd
--- dav4tbsync-0.11/locale/en-US/dav.dtd 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/en-US/dav.dtd 2019-02-26 16:26:01.000000000 +0000
@@ -6,6 +6,7 @@
+
@@ -30,15 +31,30 @@
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -Nru dav4tbsync-0.11/locale/en-US/dav.strings dav4tbsync-0.15/locale/en-US/dav.strings
--- dav4tbsync-0.11/locale/en-US/dav.strings 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/en-US/dav.strings 2019-02-26 16:26:01.000000000 +0000
@@ -1,5 +1,8 @@
menu.name = CalDAV & CardDAV
+defaultname.calendar=Calendar
+defaultname.contacts=Contacts
+
syncstate.syncing=Initialize synchronization
syncstate.preparing=Preparing next item for synchronization
syncstate.done=Preparing next item for synchronization
@@ -26,6 +29,7 @@
status.401=Could not authenticate, check username and password.
status.500=Unknown Server Error (HTTP Error 500).
status.503=Service unavailable.
+helplink.503=https://github.com/jobisoft/DAV-4-TbSync/issues/41
status.httperror=Communication error (HTTP status ##replace.1##).
status.OK=OK
status.nolightning=Lightning add-on not installed, calendars are not supported.
@@ -41,8 +45,11 @@
status.OK.caldav-calendar-already-exists=Calendar already exists
status.info.restored=Due to partially missing write permissions, some actions were rejected by the server and reversed locally.
status.maiformed-xml=Could not parse XML. Check error log for details.
+helplink.maiformed-xml=https://github.com/jobisoft/DAV-4-TbSync/issues/59#issuecomment-459685281
status.service-discovery-failed=sabre/dav service discovery failed for this server.
status.missing-permission=Missing permission: ##replace.1##
+status.caldavservernotfound=Could not find a CalDAV server.
+status.carddavservernotfound=Could not find a CardDAV server.
status.OK.managedbylightning=Calendar managed by Lightning.
diff -Nru dav4tbsync-0.11/locale/hu/dav.dtd dav4tbsync-0.15/locale/hu/dav.dtd
--- dav4tbsync-0.11/locale/hu/dav.dtd 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/hu/dav.dtd 2019-02-26 16:26:01.000000000 +0000
@@ -6,6 +6,7 @@
+
@@ -29,16 +30,31 @@
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -Nru dav4tbsync-0.11/locale/hu/dav.strings dav4tbsync-0.15/locale/hu/dav.strings
--- dav4tbsync-0.11/locale/hu/dav.strings 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/hu/dav.strings 2019-02-26 16:26:01.000000000 +0000
@@ -1,5 +1,8 @@
menu.name = CalDAV és CardDAV
+defaultname.calendar=naptár
+defaultname.contacts=kapcsolatok
+
syncstate.syncing=Összehangolás előkészítése
syncstate.preparing=Az összehangolás következő elem előkészítése
syncstate.done=Az összehangolás következő elem előkészítése
@@ -26,6 +29,7 @@
status.401=Nem sikerült hitelesíteni, ellenőrizni a felhasználónevet és a jelszót.
status.500=500-as hibakód (belső kiszolgálóhiba).
status.503=503-as hibakód (a szolgáltatás nem érhető el).
+helplink.503=https://github.com/jobisoft/DAV-4-TbSync/issues/41
status.httperror=Kommunikációs hiba (HTTP állapot ##replace.1##).
status.OK=Rendben
status.nolightning=A Lightning-t kiegészítő nincs telepítve, a naptárak nem támogatottak.
@@ -41,8 +45,11 @@
status.OK.caldav-calendar-already-exists=A naptár már létezik
status.info.restored=A hiányzó írási jogosultságok miatt egyes műveleteket a szerver elutasított, és helyileg visszafordított.
status.maiformed-xml=A válasz hibás formátumú XML-t tartalmaz, amely nem tudja feldolgozni a összehangolást.
+helplink.maiformed-xml=https://github.com/jobisoft/DAV-4-TbSync/issues/59#issuecomment-459685281
status.service-discovery-failed=A kiszolgálónak nem sikerült a sabre/dav szolgáltatási felderítése.
status.missing-permission=Hiányzó engedély: ##replace.1##
+status.caldavservernotfound=Nem található CalDAV szerver.
+status.carddavservernotfound=Nem található CardDAV szerver.
status.OK.managedbylightning=A Lightning-t által kezelt naptár.
diff -Nru dav4tbsync-0.11/locale/it/dav.dtd dav4tbsync-0.15/locale/it/dav.dtd
--- dav4tbsync-0.11/locale/it/dav.dtd 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/it/dav.dtd 2019-02-26 16:26:01.000000000 +0000
@@ -6,6 +6,7 @@
+
@@ -29,16 +30,31 @@
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -Nru dav4tbsync-0.11/locale/it/dav.strings dav4tbsync-0.15/locale/it/dav.strings
--- dav4tbsync-0.11/locale/it/dav.strings 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/it/dav.strings 2019-02-26 16:26:01.000000000 +0000
@@ -1,5 +1,8 @@
menu.name = CalDAV & CardDAV
+defaultname.calendar=calendario
+defaultname.contacts=contatti
+
syncstate.syncing=Inizializzazione sincronizzazione in corso
syncstate.preparing=Preparazione prossimo elemento per la sincronizzazione in corso
syncstate.done=Preparazione prossimo elemento per la sincronizzazione in corso
@@ -26,6 +29,7 @@
status.401=Impossibile autenticarsi, controllare nome utente e password.
status.500=Errore server sconosciuto (errore HTTP 500).
status.503=Servizio non disponibile.
+helplink.503=https://github.com/jobisoft/DAV-4-TbSync/issues/41
status.httperror=Errore di comunicazione (stato HTTP ##replace.1##).
status.OK=OK
status.nolightning=Componente aggiuntivo Lightning non installato, i calendari non sono supportati.
@@ -41,8 +45,11 @@
status.OK.caldav-calendar-already-exists=Il calendario esiste già
status.info.restored=A causa della mancanza di permessi di scrittura, alcune azioni sono state respinte dal server e annullate localmente.
status.maiformed-xml=La risposta contiene XML malformato, impossibile gestirla, sincronizzazione interrotta.
+helplink.maiformed-xml=https://github.com/jobisoft/DAV-4-TbSync/issues/59#issuecomment-459685281
status.service-discovery-failed=Rilevamento servizio Sabre/DAV non riuscito per questo server.
status.missing-permission=Permesso mancante: ##replace.1##
+status.caldavservernotfound=Impossibile trovare un server CalDAV.
+status.carddavservernotfound=Impossibile trovare un server CardDAV.
status.OK.managedbylightning=Calendario gestito da Lightning.
diff -Nru dav4tbsync-0.11/locale/pt-BR/dav.dtd dav4tbsync-0.15/locale/pt-BR/dav.dtd
--- dav4tbsync-0.11/locale/pt-BR/dav.dtd 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/pt-BR/dav.dtd 2019-02-26 16:26:01.000000000 +0000
@@ -6,6 +6,7 @@
+
@@ -29,16 +30,31 @@
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -Nru dav4tbsync-0.11/locale/pt-BR/dav.strings dav4tbsync-0.15/locale/pt-BR/dav.strings
--- dav4tbsync-0.11/locale/pt-BR/dav.strings 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/pt-BR/dav.strings 2019-02-26 16:26:01.000000000 +0000
@@ -1,5 +1,8 @@
menu.name = CalDAV e CardDAV
+defaultname.calendar=calendário
+defaultname.contacts=contatos
+
syncstate.syncing=Inicializar sincronização
syncstate.preparing=Preparando o próximo item para sincronização
syncstate.done=Preparando o próximo item para sincronização
@@ -26,6 +29,7 @@
status.401=Não foi possível autenticar, verifique o nome de usuário e a senha.
status.500=Erro de servidor desconhecido (HTTP Erro 500).
status.503=Serviço indisponível.
+helplink.503=https://github.com/jobisoft/DAV-4-TbSync/issues/41
status.httperror=Erro de comunicação (HTTP status ##replace.1##).
status.OK=OK
status.nolightning=O complemento Lightning não está instalado, os calendários não serão suportados.
@@ -41,8 +45,11 @@
status.OK.caldav-calendar-already-exists=O calendário já existe
status.info.restored=Devido à falta parcial de permissões de gravação, algumas ações foram rejeitadas pelo servidor e revertidas localmente.
status.maiformed-xml=Não foi possível analisar o retorno XML. Verifique o log de erros para mais detalhes.
+helplink.maiformed-xml=https://github.com/jobisoft/DAV-4-TbSync/issues/59#issuecomment-459685281
status.service-discovery-failed=A descoberta do serviço sabre/dav falhou para este servidor.
status.missing-permission=Permissão ausente: ##replace.1##
+status.caldavservernotfound=Não foi possível encontrar um servidor CalDAV.
+status.carddavservernotfound=Não foi possível encontrar um servidor CardDAV.
status.OK.managedbylightning=Calendário gerenciado pelo Lightning.
diff -Nru dav4tbsync-0.11/locale/ru/dav.dtd dav4tbsync-0.15/locale/ru/dav.dtd
--- dav4tbsync-0.11/locale/ru/dav.dtd 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/ru/dav.dtd 2019-02-26 16:26:01.000000000 +0000
@@ -6,6 +6,7 @@
+
@@ -31,14 +32,29 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -Nru dav4tbsync-0.11/locale/ru/dav.strings dav4tbsync-0.15/locale/ru/dav.strings
--- dav4tbsync-0.11/locale/ru/dav.strings 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/locale/ru/dav.strings 2019-02-26 16:26:01.000000000 +0000
@@ -1,5 +1,8 @@
menu.name = CalDAV & CardDAV
+defaultname.calendar=календарь
+defaultname.contacts=контакты
+
syncstate.syncing=Инициализация синхронизации
syncstate.preparing=Подготовка следующего элемента для синхронизации
syncstate.done=Подготовка следующего элемента для синхронизации
@@ -26,6 +29,7 @@
status.401=Не удалось аутентифицировать, проверить имя пользователя и пароль. (HTTP Ошибка 401).
status.500=Неизвестная ошибка сервера (HTTP Ошибка 500).
status.503=Сервис недоступен (HTTP Ошибка 503).
+helplink.503=https://github.com/jobisoft/DAV-4-TbSync/issues/41
status.httperror=Ошибка связи (HTTP статус ##replace.1##).
status.OK=Готово
status.nolightning=Lightning Add-On не установлен, календари не поддерживаются.
@@ -41,8 +45,11 @@
status.OK.caldav-calendar-already-exists=Календарь уже существует
status.info.restored=Из-за частично отсутствующих разрешений на запись некоторые действия были отклонены сервером и отменены локально.
status.maiformed-xml=Ответ содержит бесформенные XML, обработка невозможна, синхронизация прервана.
+helplink.maiformed-xml=https://github.com/jobisoft/DAV-4-TbSync/issues/59#issuecomment-459685281
status.service-discovery-failed=Для этого сервера не удалось обнаружить службу sabre/dav.
status.missing-permission=Отсутствует разрешение: ##replace.1##
+status.caldavservernotfound=Не удалось найти сервер CalDAV.
+status.carddavservernotfound=Не удалось найти сервер CardDAV.
status.OK.managedbylightning=Календарь управляется lightning.
diff -Nru dav4tbsync-0.11/Makebeta dav4tbsync-0.15/Makebeta
--- dav4tbsync-0.11/Makebeta 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/Makebeta 2019-02-26 16:26:01.000000000 +0000
@@ -20,14 +20,14 @@
sed -i "s/%VERSION%/$version/g" "beta-release-channel-update.rdf"
sed -i "s|%LINK%|$1/$3|g" "beta-release-channel-update.rdf"
-if [ $vlevels -gt 1 ]
-then
- echo "Version $version is a beta release (will include updateURL)"
+#if [ $vlevels -gt 1 ]
+#then
+ echo "Releasing version $version via beta release channel (will include updateURL)"
sed -i "s| | \n $1/$4|g" "install.rdf"
sed -i "s|| [Beta Release Channel]|g" "install.rdf"
-else
- echo "Version $version is a stable release (will NOT include updateURL)"
-fi
+#else
+# echo "Version $version is a stable release (will NOT include updateURL)"
+#fi
cp beta-release-channel-update.rdf $2/$4
diff -Nru dav4tbsync-0.11/README.md dav4tbsync-0.15/README.md
--- dav4tbsync-0.11/README.md 2019-01-31 18:54:04.000000000 +0000
+++ dav4tbsync-0.15/README.md 2019-02-26 16:26:01.000000000 +0000
@@ -10,3 +10,14 @@
* [icloud16.png] by [Five Icons](https://www.iconfinder.com/icons/252111/apple_icon)
* [yahoo16.png] by [Five Icons](https://www.iconfinder.com/icons/252070/yahoo_icon)
* [gmx16.png] by [CloudSponge](https://www.iconfinder.com/icons/1175604/address_book_contact_contacts_email_gmx_square_icon)
+* [type.pref.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671863/)
+* [type.other.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671671/)
+* [type.work.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671695/)
+* [type.home.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671775/)
+* [type.car.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671885/)
+* [type.cell.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671810/)
+* [type.fax.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671840/)
+* [type.video.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671900/)
+* [type.voice.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671831/)
+* [type.pager.png] by [Steve Schoger](https://www.iconfinder.com/icons/3671720/)
+
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/screenshots/AddAccount.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/screenshots/AddAccount.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/screenshots/options.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/screenshots/options.png differ
diff -Nru dav4tbsync-0.11/skin/ab.css dav4tbsync-0.15/skin/ab.css
--- dav4tbsync-0.11/skin/ab.css 1970-01-01 00:00:00.000000000 +0000
+++ dav4tbsync-0.15/skin/ab.css 2019-02-26 16:26:01.000000000 +0000
@@ -0,0 +1,48 @@
+/* skin/toolbar-button.css */
+
+treechildren::-moz-tree-image(DirCol, davfruux) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://dav4tbsync/skin/fruux16.png");
+}
+
+treechildren::-moz-tree-image(DirCol, davicloud) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://dav4tbsync/skin/icloud16.png");
+}
+
+treechildren::-moz-tree-image(DirCol, davyahoo) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://dav4tbsync/skin/yahoo16.png");
+}
+
+treechildren::-moz-tree-image(DirCol, davgmx) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://dav4tbsync/skin/gmx16.png");
+}
+
+treechildren::-moz-tree-image(DirCol, davcustom) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://dav4tbsync/skin/sabredav16.png");
+}
+
+
+.abMenuItem[AddrBook="true"][TbSyncIcon="davfruux"] {
+ list-style-image: url("chrome://dav4tbsync/skin/fruux16.png");
+}
+
+.abMenuItem[AddrBook="true"][TbSyncIcon="davicloud"] {
+ list-style-image: url("chrome://dav4tbsync/skin/icloud16.png");
+}
+
+.abMenuItem[AddrBook="true"][TbSyncIcon="davyahoo"] {
+ list-style-image: url("chrome://dav4tbsync/skin/yahoo16.png");
+}
+
+.abMenuItem[AddrBook="true"][TbSyncIcon="davgmx"] {
+ list-style-image: url("chrome://dav4tbsync/skin/gmx16.png");
+}
+
+
+.abMenuItem[AddrBook="true"][TbSyncIcon="davcustom"] {
+ list-style-image: url("chrome://dav4tbsync/skin/sabredav16.png");
+}
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/arrow.down10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/arrow.down10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/arrow.up10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/arrow.up10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.car10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.car10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.car16.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.car16.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.cell10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.cell10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.cell16.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.cell16.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.fax10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.fax10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.fax16.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.fax16.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.home10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.home10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.home16.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.home16.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.nopref.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.nopref.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.other10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.other10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.other16.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.other16.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.pager10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.pager10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.pager16.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.pager16.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.pref.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.pref.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.video10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.video10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.video16.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.video16.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.voice10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.voice10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.voice16.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.voice16.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.work10.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.work10.png differ
Binary files /tmp/tmpN48Dzk/p2P6w6_nLP/dav4tbsync-0.11/skin/type.work16.png and /tmp/tmpN48Dzk/FwlsCAMQt6/dav4tbsync-0.15/skin/type.work16.png differ