From bc800ddc301fc79eac3fb5b2224fb07314a0251f Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Mon, 18 Feb 2013 19:34:04 -0500 Subject: [PATCH] Fix WebDAV verification feedback --- .../content/zotero/preferences/preferences.js | 73 ++++- chrome/content/zotero/xpcom/storage.js | 27 +- chrome/content/zotero/xpcom/storage/webdav.js | 265 ++++++++---------- 3 files changed, 187 insertions(+), 178 deletions(-) diff --git a/chrome/content/zotero/preferences/preferences.js b/chrome/content/zotero/preferences/preferences.js index 9086f0dbb..ec9e41d4e 100644 --- a/chrome/content/zotero/preferences/preferences.js +++ b/chrome/content/zotero/preferences/preferences.js @@ -371,38 +371,83 @@ function verifyStorageServer() { abortButton.hidden = false; progressMeter.hidden = false; - var requestHolder = Zotero.Sync.Storage.WebDAV.checkServer(function (uri, status, callback) { + var request = null; + var onDone = false; + + Zotero.Sync.Storage.WebDAV.checkServer() + .finally(function () { verifyButton.hidden = false; abortButton.hidden = true; progressMeter.hidden = true; - + }) + .spread(function (uri, status) { switch (status) { case Zotero.Sync.Storage.ERROR_NO_URL: - setTimeout(function () { + onDone = function () { urlField.focus(); - }, 1); + }; break; case Zotero.Sync.Storage.ERROR_NO_USERNAME: - setTimeout(function () { + onDone = function () { usernameField.focus(); - }, 1); + }; break; case Zotero.Sync.Storage.ERROR_NO_PASSWORD: - setTimeout(function () { - passwordField.focus(); - }, 1); + case Zotero.Sync.Storage.ERROR_AUTH_FAILED: + onDone = function () { + passwordField.focus; + } break; } - Zotero.Sync.Storage.WebDAV.checkServerCallback(uri, status, window); - }); + return Zotero.Sync.Storage.WebDAV.checkServerCallback(uri, status, window); + }) + .then(function (success) { + if (success) { + Zotero.debug("WebDAV verification succeeded"); + + var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + promptService.alert( + window, + Zotero.getString('sync.storage.serverConfigurationVerified'), + Zotero.getString('sync.storage.fileSyncSetUp') + ); + Zotero.Prefs.set("sync.storage.verified", true); + } + else { + Zotero.debug("WebDAV verification failed"); + if (onDone) { + setTimeout(function () { + onDone(); + }, 1); + } + } + }) + .progress(function (obj) { + request = obj.request; + }) + .catch(function (e) { + Zotero.debug("WebDAV verification failed"); + Zotero.debug(e, 1); + Components.utils.reportError(e); + Zotero.Utilities.Internal.errorPrompt(Zotero.getString('general.error'), e); + + if (onDone) { + setTimeout(function () { + onDone(); + }, 1); + } + }) + .done(); abortButton.onclick = function () { - if (requestHolder.request) { - requestHolder.request.onreadystatechange = undefined; - requestHolder.request.abort(); + if (request) { + Zotero.debug("Cancelling verification request"); + request.onreadystatechange = undefined; + request.abort(); verifyButton.hidden = false; abortButton.hidden = true; progressMeter.hidden = true; diff --git a/chrome/content/zotero/xpcom/storage.js b/chrome/content/zotero/xpcom/storage.js index 5f17b817c..1f7f2e87f 100644 --- a/chrome/content/zotero/xpcom/storage.js +++ b/chrome/content/zotero/xpcom/storage.js @@ -114,10 +114,10 @@ Zotero.Sync.Storage = new function () { Zotero.debug("WebDAV file sync is not active"); // Try to verify server now if it hasn't been - return mode.checkServerPromise() - .then(function () { - libraryModes[0] = Zotero.Sync.Storage.WebDAV; - }); + return Zotero.Sync.Storage.checkServerPromise(Zotero.Sync.Storage.WebDAV) + .then(function () { + libraryModes[0] = Zotero.Sync.Storage.WebDAV; + }); } libraryModes[0] = Zotero.Sync.Storage.WebDAV; @@ -178,7 +178,6 @@ Zotero.Sync.Storage = new function () { promises.forEach(function (p) { p = p.valueOf(); var libraryID = p[0].valueOf(); - Zotero.debug(libraryID); if (p[1].isFulfilled()) { librarySyncTimes[libraryID] = p[1].valueOf(); } @@ -953,18 +952,14 @@ Zotero.Sync.Storage = new function () { this.checkServerPromise = function (mode) { - var deferred = Q.defer(); - mode.checkServer(function (uri, status) { + return mode.checkServer() + .spread(function (uri, status) { var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator); var lastWin = wm.getMostRecentWindow("navigator:browser"); var success = mode.checkServerCallback(uri, status, lastWin, true); - if (success) { - Zotero.debug(mode.name + " file sync is successfully set up"); - Q.resolve(); - } - else { + if (!success) { Zotero.debug(mode.name + " verification failed"); var e = new Zotero.Error( @@ -980,11 +975,13 @@ Zotero.Sync.Storage = new function () { } } ); - - Q.reject(e); + throw e; } + }) + .then(function () { + Zotero.debug(mode.name + " file sync is successfully set up"); + Zotero.Prefs.set("sync.storage.verified", true); }); - return deferred.promise; } diff --git a/chrome/content/zotero/xpcom/storage/webdav.js b/chrome/content/zotero/xpcom/storage/webdav.js index c14edab1a..abaf105df 100644 --- a/chrome/content/zotero/xpcom/storage/webdav.js +++ b/chrome/content/zotero/xpcom/storage/webdav.js @@ -55,7 +55,8 @@ Zotero.Sync.Storage.WebDAV = (function () { ) .then(function (req) { checkResponse(req); - + }) + .then(function (req) { var funcName = "Zotero.Sync.Storage.WebDAV.getStorageModificationTime()"; // mod_speling can return 300s for 404s with base name matches @@ -385,28 +386,22 @@ Zotero.Sync.Storage.WebDAV = (function () { switch (req.status) { case 201: - callback(uri, Zotero.Sync.Storage.SUCCESS); - break; + return [uri, Zotero.Sync.Storage.SUCCESS]; case 401: - callback(uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED); - return; + return [uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED]; case 403: - callback(uri, Zotero.Sync.Storage.ERROR_FORBIDDEN); - return; + return [uri, Zotero.Sync.Storage.ERROR_FORBIDDEN]; case 405: - callback(uri, Zotero.Sync.Storage.ERROR_NOT_ALLOWED); - return; + return [uri, Zotero.Sync.Storage.ERROR_NOT_ALLOWED]; case 500: - callback(uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR); - return; + return [uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR]; default: - callback(uri, Zotero.Sync.Storage.ERROR_UNKNOWN); - return; + return [uri, Zotero.Sync.Storage.ERROR_UNKNOWN]; } }); } @@ -585,7 +580,7 @@ Zotero.Sync.Storage.WebDAV = (function () { /** - * Checks for an invalid SSL certificate and displays a nice error + * Checks for an invalid SSL certificate and throws a nice error */ function checkResponse(req) { var channel = req.channel; @@ -981,41 +976,41 @@ Zotero.Sync.Storage.WebDAV = (function () { return Q.fcall(function () { return self._cacheCredentials(); }) - .then(function () { - var lastSyncURI = this.rootURI; - lastSyncURI.spec += "lastsync"; - return Zotero.HTTP.promise("GET", lastSyncURI, - { debug: true, successCodes: [200, 404] }); - }) - .then(function (req) { - if (req.status == 404) { - Zotero.debug("No last WebDAV sync time"); - return null; - } - - var lastModified = req.getResponseHeader("Last-Modified"); - var date = new Date(lastModified); - Zotero.debug("Last successful WebDAV sync was " + date); - return Zotero.Date.toUnixTimestamp(date); - }) - .fail(function (e) { - if (e instanceof Zotero.HTTP.UnexpectedStatusException) { - if (e.status == 403) { - Zotero.debug("Clearing WebDAV authentication credentials", 2); - _cachedCredentials = false; - } - else { - throw("Unexpected status code " + e.status + " getting " - + "WebDAV last sync time"); + .then(function () { + var lastSyncURI = this.rootURI; + lastSyncURI.spec += "lastsync"; + return Zotero.HTTP.promise("GET", lastSyncURI, + { debug: true, successCodes: [200, 404] }); + }) + .then(function (req) { + if (req.status == 404) { + Zotero.debug("No last WebDAV sync time"); + return null; } - return Q.reject(e); - } - // TODO: handle browser offline exception - else { - throw (e); - } - }); + var lastModified = req.getResponseHeader("Last-Modified"); + var date = new Date(lastModified); + Zotero.debug("Last successful WebDAV sync was " + date); + return Zotero.Date.toUnixTimestamp(date); + }) + .fail(function (e) { + if (e instanceof Zotero.HTTP.UnexpectedStatusException) { + if (e.status == 403) { + Zotero.debug("Clearing WebDAV authentication credentials", 2); + _cachedCredentials = false; + } + else { + throw("Unexpected status code " + e.status + " getting " + + "WebDAV last sync time"); + } + + return Q.reject(e); + } + // TODO: handle browser offline exception + else { + throw (e); + } + }); }; @@ -1068,31 +1063,29 @@ Zotero.Sync.Storage.WebDAV = (function () { } return Zotero.HTTP.promise("OPTIONS", this.rootURI) - .then(function (req) { - // TODO: promisify - checkResponse(req); - - Zotero.debug("Credentials are cached"); - _cachedCredentials = true; - }) - .fail(function (e) { - if (e instanceof Zotero.HTTP.UnexpectedStatusException) { - var msg = "Unexpected status code " + e.status + " " - + "for OPTIONS request caching WebDAV credentials"; - Zotero.debug(msg, 1); - Components.utils.reportError(msg); - throw new Error(_defaultErrorRestart); - } - throw e; - }); + .then(function (req) { + return checkResponse(req); + }) + .then(function () { + Zotero.debug("Credentials are cached"); + _cachedCredentials = true; + }) + .fail(function (e) { + if (e instanceof Zotero.HTTP.UnexpectedStatusException) { + var msg = "Unexpected status code " + e.status + " " + + "for OPTIONS request caching WebDAV credentials"; + Zotero.debug(msg, 1); + Components.utils.reportError(msg); + throw new Error(_defaultErrorRestart); + } + throw e; + }); }; - /** - * @param {Function} callback Function to pass URI and result value to - * @param {Object} errorCallbacks - */ - obj._checkServer = function (callback) { + obj._checkServer = function () { + var deferred = Q.defer(); + try { // Clear URIs this.init(); @@ -1103,27 +1096,23 @@ Zotero.Sync.Storage.WebDAV = (function () { catch (e) { switch (e.name) { case 'Z_ERROR_NO_URL': - callback(null, Zotero.Sync.Storage.ERROR_NO_URL); - return; + deferred.resolve([null, Zotero.Sync.Storage.ERROR_NO_URL]); case 'Z_ERROR_NO_USERNAME': - callback(null, Zotero.Sync.Storage.ERROR_NO_USERNAME); - return; + deferred.resolve([null, Zotero.Sync.Storage.ERROR_NO_USERNAME]); case 'Z_ERROR_NO_PASSWORD': - callback(null, Zotero.Sync.Storage.ERROR_NO_PASSWORD); - return; + deferred.resolve([null, Zotero.Sync.Storage.ERROR_NO_PASSWORD]); default: Zotero.debug(e); Components.utils.reportError(e); - callback(null, Zotero.Sync.Storage.ERROR_UNKNOWN); - return; + deferred.resolve([null, Zotero.Sync.Storage.ERROR_UNKNOWN]); } + + return deferred.promise; } - var requestHolder = { request: null }; - var xmlstr = "" // IIS 5.1 requires at least one property in PROPFIND + "" @@ -1133,10 +1122,14 @@ Zotero.Sync.Storage.WebDAV = (function () { var request = Zotero.HTTP.doOptions(uri, function (req) { // Timeout if (req.status == 0) { - checkResponse(req); + try { + checkResponse(req); + } + catch (e) { + deferred.reject(e); + } - callback(uri, Zotero.Sync.Storage.ERROR_UNREACHABLE); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_UNREACHABLE]); } Zotero.debug(req.getAllResponseHeaders()); @@ -1145,26 +1138,21 @@ Zotero.Sync.Storage.WebDAV = (function () { switch (req.status) { case 400: - callback(uri, Zotero.Sync.Storage.ERROR_BAD_REQUEST); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_BAD_REQUEST]); case 401: - callback(uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED]); case 403: - callback(uri, Zotero.Sync.Storage.ERROR_FORBIDDEN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_FORBIDDEN]); case 500: - callback(uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR]); } var dav = req.getResponseHeader("DAV"); if (dav == null) { - callback(uri, Zotero.Sync.Storage.ERROR_NOT_DAV); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_NOT_DAV]); } // Get the Authorization header used in case we need to do a request @@ -1209,32 +1197,26 @@ Zotero.Sync.Storage.WebDAV = (function () { switch (req.status) { case 200: // IIS 5.1 and Sakai return 200 case 204: - callback(uri, Zotero.Sync.Storage.SUCCESS); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.SUCCESS]); case 401: - callback(uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED]); case 403: - callback(uri, Zotero.Sync.Storage.ERROR_FORBIDDEN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_FORBIDDEN]); default: - callback(uri, Zotero.Sync.Storage.ERROR_UNKNOWN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_UNKNOWN]); } } ); return; case 401: - callback(uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED]); case 403: - callback(uri, Zotero.Sync.Storage.ERROR_FORBIDDEN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_FORBIDDEN]); // This can happen with cloud storage services // backed by S3 or other eventually consistent @@ -1244,51 +1226,41 @@ Zotero.Sync.Storage.WebDAV = (function () { // not to serve extensionless files or .prop files // http://support.microsoft.com/kb/326965 case 404: - callback(uri, Zotero.Sync.Storage.ERROR_FILE_MISSING_AFTER_UPLOAD); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_FILE_MISSING_AFTER_UPLOAD]); case 500: - callback(uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR]); default: - callback(uri, Zotero.Sync.Storage.ERROR_UNKNOWN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_UNKNOWN]); } } ); return; case 401: - callback(uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED]); case 403: - callback(uri, Zotero.Sync.Storage.ERROR_FORBIDDEN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_FORBIDDEN]); case 500: - callback(uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR]); default: - callback(uri, Zotero.Sync.Storage.ERROR_UNKNOWN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_UNKNOWN]); } }); return; case 400: - callback(uri, Zotero.Sync.Storage.ERROR_BAD_REQUEST); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_BAD_REQUEST]); case 401: - callback(uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED]); case 403: - callback(uri, Zotero.Sync.Storage.ERROR_FORBIDDEN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_FORBIDDEN]); case 404: // Include Authorization header from /zotero request, @@ -1309,49 +1281,52 @@ Zotero.Sync.Storage.WebDAV = (function () { switch (req.status) { // Parent directory existed case 207: - callback(uri, Zotero.Sync.Storage.ERROR_ZOTERO_DIR_NOT_FOUND); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_ZOTERO_DIR_NOT_FOUND]); case 400: - callback(uri, Zotero.Sync.Storage.ERROR_BAD_REQUEST); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_BAD_REQUEST]); case 401: - callback(uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_AUTH_FAILED]); // Parent directory wasn't found either case 404: - callback(uri, Zotero.Sync.Storage.ERROR_PARENT_DIR_NOT_FOUND); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_PARENT_DIR_NOT_FOUND]); default: - callback(uri, Zotero.Sync.Storage.ERROR_UNKNOWN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_UNKNOWN]); } }, newHeaders); return; case 500: - callback(uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_SERVER_ERROR]); default: - callback(uri, Zotero.Sync.Storage.ERROR_UNKNOWN); - return; + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_UNKNOWN]); } }, headers); }); if (!request) { - callback(uri, Zotero.Sync.Storage.ERROR_OFFLINE); + return deferred.resolve([uri, Zotero.Sync.Storage.ERROR_OFFLINE]); } - requestHolder.request = request; - return requestHolder; + // Pass request to progress handler + var obj = {}; + obj.request = request; + deferred.notify(obj) + + + return deferred.promise; }; + /** + * Handles the result of WebDAV verification, displaying an alert if necessary. + * + * @return bool True if the verification succeeded, false otherwise + */ obj._checkServerCallback = function (uri, status, window, skipSuccessMessage) { var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. @@ -1362,14 +1337,6 @@ Zotero.Sync.Storage.WebDAV = (function () { switch (status) { case Zotero.Sync.Storage.SUCCESS: - if (!skipSuccessMessage) { - promptService.alert( - window, - Zotero.getString('sync.storage.serverConfigurationVerified'), - Zotero.getString('sync.storage.fileSyncSetUp') - ); - } - Zotero.Prefs.set("sync.storage.verified", true); return true; case Zotero.Sync.Storage.ERROR_NO_URL: