Shower nicer error for malformed WebDAV URL

We fix `://` or `//` automatically after #3483, but a leading ':'
character would still show an internal `NS_ERROR_MALFORMED_URI` error.
Instead, just say "[url] is not a valid WebDAV URL".
This commit is contained in:
Dan Stillman 2024-12-10 03:02:03 -05:00
parent 320399dca9
commit 77bdb0b4cb
3 changed files with 89 additions and 20 deletions

View file

@ -1086,9 +1086,16 @@ Zotero.HTTP = new function() {
};
this.getDisplayURI = function (uri) {
this.getDisplayURI = function (uri, noCredentials) {
if (!uri.password) return uri;
return uri.mutate().setPassword('********').finalize();
uri = uri.mutate();
if (noCredentials) {
uri.setUserPass('');
}
else {
uri.setPassword('********');
}
return uri.finalize();
}

View file

@ -31,10 +31,10 @@ if (!Zotero.Sync.Storage.Mode) {
Zotero.Sync.Storage.Mode.WebDAV = function (options) {
this.options = options;
this.VerificationError = function (error, uri) {
this.VerificationError = function (error, url) {
this.message = `WebDAV verification error (${error})`;
this.error = error;
this.uri = uri;
this.url = url;
}
this.VerificationError.prototype = Object.create(Error.prototype);
}
@ -196,14 +196,23 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
throw new this.VerificationError("NO_PASSWORD");
}
url = scheme + '://'
var spec = scheme + '://'
+ encodeURIComponent(username) + ':' + encodeURIComponent(password) + '@'
+ url
+ (url.endsWith('/') ? '' : '/');
var io = Services.io;
this._parentURI = io.newURI(url, null, null);
this._rootURI = io.newURI(url + "zotero/", null, null);
try {
this._parentURI = io.newURI(spec, null, null);
}
catch (e) {
if (e.message.includes('NS_ERROR_MALFORMED_URI')) {
let displayURL = scheme + '://' + url + (url.endsWith('/') ? '' : '/');
throw new this.VerificationError("INVALID_URL", displayURL);
}
throw e;
}
this._rootURI = io.newURI(spec + "zotero/", null, null);
Zotero.HTTP.CookieBlocker.addURL(this._rootURI.spec);
},
@ -641,7 +650,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
var dav = req.getResponseHeader("DAV");
if (dav == null) {
throw new this.VerificationError("NOT_DAV", uri);
throw new this.VerificationError("NOT_DAV", Zotero.HTTP.getDisplayURI(uri, true).spec);
}
var headers = { Depth: 0 };
@ -683,7 +692,10 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
catch (e) {
if (e instanceof Zotero.HTTP.UnexpectedStatusException) {
if (e.status >= 200 && e.status < 300) {
throw new this.VerificationError("NONEXISTENT_FILE_NOT_MISSING", uri);
throw new this.VerificationError(
"NONEXISTENT_FILE_NOT_MISSING",
Zotero.HTTP.getDisplayURI(uri, true).spec
);
}
}
throw e;
@ -730,7 +742,10 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
// This can also be from IIS 6+, which is configured not to serve .prop files.
// http://support.microsoft.com/kb/326965
else if (req.status == 404) {
throw new this.VerificationError("FILE_MISSING_AFTER_UPLOAD", uri);
throw new this.VerificationError(
"FILE_MISSING_AFTER_UPLOAD",
Zotero.HTTP.getDisplayURI(uri, true).spec
);
}
}
else if (req.status == 404) {
@ -751,10 +766,16 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
});
if (req.status == 207) {
throw new this.VerificationError("ZOTERO_DIR_NOT_FOUND", uri);
throw new this.VerificationError(
"ZOTERO_DIR_NOT_FOUND",
Zotero.HTTP.getDisplayURI(uri, true).spec
);
}
else if (req.status == 404) {
throw new this.VerificationError("PARENT_DIR_NOT_FOUND", uri);
throw new this.VerificationError(
"PARENT_DIR_NOT_FOUND",
Zotero.HTTP.getDisplayURI(uri, true).spec
);
}
}
@ -805,10 +826,6 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
}
}
else if (err instanceof this.VerificationError) {
let spec;
if (err.uri) {
spec = err.uri.scheme + '://' + err.uri.hostPort + err.uri.pathQueryRef;
}
switch (err.error) {
case "NO_URL":
errorMsg = Zotero.getString('sync.storage.error.webdav.enterURL');
@ -822,21 +839,22 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
errorMsg = Zotero.getString('sync.error.enterPassword');
break;
case "INVALID_URL":
case "NOT_DAV":
errorMsg = Zotero.getString('sync.storage.error.webdav.invalidURL', spec);
errorMsg = Zotero.getString('sync.storage.error.webdav.invalidURL', err.url);
break;
case "PARENT_DIR_NOT_FOUND":
errorTitle = Zotero.getString('sync.storage.error.directoryNotFound');
var parentSpec = spec.replace(/zotero\/$/, "");
errorMsg = Zotero.getString('sync.storage.error.doesNotExist', parentSpec);
var parentURL = err.url.replace(/zotero\/$/, "");
errorMsg = Zotero.getString('sync.storage.error.doesNotExist', parentURL);
break;
case "ZOTERO_DIR_NOT_FOUND":
var create = promptService.confirmEx(
window,
Zotero.getString('sync.storage.error.directoryNotFound'),
Zotero.getString('sync.storage.error.doesNotExist', spec) + "\n\n"
Zotero.getString('sync.storage.error.doesNotExist', err.url) + "\n\n"
+ Zotero.getString('sync.storage.error.createNow'),
promptService.BUTTON_POS_0
* promptService.BUTTON_TITLE_IS_STRING

View file

@ -768,6 +768,50 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
win.close();
});
it("should show an error for a non-DAV URL", async function () {
Zotero.HTTP.mock = null;
Zotero.Prefs.set("sync.storage.url", davHostPath);
httpd.registerPathHandler(
`${davBasePath}zotero/`,
{
handle: function (request, response) {
// Force Basic Auth
if (!request.hasHeader('Authorization')) {
response.setStatusLine(null, 401, null);
response.setHeader('WWW-Authenticate', 'Basic realm="WebDAV"', false);
return;
}
response.setStatusLine(null, 200, "OK");
response.write("<html><body><p>This is a non-DAV URL.</p></body></html>");
}
}
);
// Begin install procedure
var win = await loadPrefPane('sync');
var button = win.document.getElementById('storage-verify');
var spy = sinon.spy(win.Zotero_Preferences.Sync, "verifyStorageServer");
var promise1 = waitForDialog(function (dialog) {
assert.include(
dialog.document.documentElement.textContent,
Zotero.getString(
'sync.storage.error.webdav.invalidURL',
davScheme + '://' + davHostPath + 'zotero/'
)
);
});
button.click();
await promise1;
var promise2 = spy.returnValues[0];
spy.restore();
await promise2;
win.close();
});
it("should show an error for a 403", function* () {
Zotero.HTTP.mock = null;
httpd.registerPathHandler(