Better Unicode path comparison in WebDAV.purgeOrphanedStorageFiles()
This commit is contained in:
parent
83fe445f8f
commit
678a6e15cc
2 changed files with 81 additions and 18 deletions
|
@ -1013,46 +1013,40 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
|
||||||
|
|
||||||
// Absolute
|
// Absolute
|
||||||
if (href.match(/^https?:\/\//)) {
|
if (href.match(/^https?:\/\//)) {
|
||||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
let ios = Components.classes["@mozilla.org/network/io-service;1"]
|
||||||
getService(Components.interfaces.nsIIOService);
|
.getService(Components.interfaces.nsIIOService);
|
||||||
var href = ios.newURI(href, null, null);
|
href = ios.newURI(href, null, null).path;
|
||||||
href = href.path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let decodedHref = decodeURIComponent(href).normalize();
|
||||||
|
let decodedPath = decodeURIComponent(path).normalize();
|
||||||
|
|
||||||
// Skip root URI
|
// Skip root URI
|
||||||
if (href == path
|
if (decodedHref == decodedPath
|
||||||
// Some Apache servers respond with a "/zotero" href
|
// Some Apache servers respond with a "/zotero" href
|
||||||
// even for a "/zotero/" request
|
// even for a "/zotero/" request
|
||||||
|| (trailingSlash && href + '/' == path)
|
|| (trailingSlash && decodedHref + '/' == decodedPath)) {
|
||||||
// Try URL-encoded as well, as above
|
|
||||||
|| decodeURIComponent(href) == path) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (href.indexOf(path) == -1
|
if (!decodedHref.startsWith(decodedPath)) {
|
||||||
// Try URL-encoded as well, in case there's a '~' or similar
|
|
||||||
// character in the URL and the server (e.g., Sakai) is
|
|
||||||
// encoding the value
|
|
||||||
&& decodeURIComponent(href).indexOf(path) == -1) {
|
|
||||||
throw new Error(`DAV:href '${href}' does not begin with path '${path}'`);
|
throw new Error(`DAV:href '${href}' does not begin with path '${path}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
var matches = href.match(/[^\/]+$/);
|
var matches = href.match(/[^\/]+$/);
|
||||||
if (!matches) {
|
if (!matches) {
|
||||||
throw new Error(
|
throw new Error(`Unexpected href '${href}'`);
|
||||||
"Unexpected href '" + href + "' in " + funcName
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
var file = matches[0];
|
var file = matches[0];
|
||||||
|
|
||||||
if (file.indexOf('.') == 0) {
|
if (file.startsWith('.')) {
|
||||||
Zotero.debug("Skipping hidden file " + file);
|
Zotero.debug("Skipping hidden file " + file);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isLastSyncFile = file == 'lastsync.txt' || file == 'lastsync';
|
var isLastSyncFile = file == 'lastsync.txt' || file == 'lastsync';
|
||||||
if (!isLastSyncFile) {
|
if (!isLastSyncFile) {
|
||||||
if (!file.match(/\.zip$/) && !file.match(/\.prop$/)) {
|
if (!file.endsWith('.zip') && !file.endsWith('.prop')) {
|
||||||
Zotero.debug("Skipping file " + file);
|
Zotero.debug("Skipping file " + file);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -862,6 +862,75 @@ describe("Zotero.Sync.Storage.Mode.WebDAV", function () {
|
||||||
Zotero.Prefs.set("lastWebDAVOrphanPurge", Math.round(new Date().getTime() / 1000) - 3600);
|
Zotero.Prefs.set("lastWebDAVOrphanPurge", Math.round(new Date().getTime() / 1000) - 3600);
|
||||||
yield assert.eventually.equal(controller.purgeOrphanedStorageFiles(), false);
|
yield assert.eventually.equal(controller.purgeOrphanedStorageFiles(), false);
|
||||||
assertRequestCount(0);
|
assertRequestCount(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("should handle unnormalized Unicode characters", function* () {
|
||||||
|
var library = Zotero.Libraries.userLibrary;
|
||||||
|
library.updateLastSyncTime();
|
||||||
|
yield library.saveTx();
|
||||||
|
|
||||||
|
const daysBeforeSyncTime = 7;
|
||||||
|
|
||||||
|
var beforeTime = new Date(Date.now() - (daysBeforeSyncTime * 86400 * 1000 + 1)).toUTCString();
|
||||||
|
var currentTime = new Date(Date.now() - 3600000).toUTCString();
|
||||||
|
|
||||||
|
var strC = '\u1E9B\u0323';
|
||||||
|
var encodedStrC = encodeURIComponent(strC);
|
||||||
|
var strD = '\u1E9B\u0323'.normalize('NFD');
|
||||||
|
var encodedStrD = encodeURIComponent(strD);
|
||||||
|
|
||||||
|
setResponse({
|
||||||
|
method: "PROPFIND",
|
||||||
|
url: `${encodedStrC}/zotero/`,
|
||||||
|
status: 207,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": 'text/xml; charset="utf-8"'
|
||||||
|
},
|
||||||
|
text: '<?xml version="1.0" encoding="utf-8"?>'
|
||||||
|
+ '<D:multistatus xmlns:D="DAV:" xmlns:ns0="DAV:">'
|
||||||
|
+ '<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">'
|
||||||
|
+ `<D:href>${davBasePath}${encodedStrD}/zotero/</D:href>`
|
||||||
|
+ '<D:propstat>'
|
||||||
|
+ '<D:prop>'
|
||||||
|
+ `<lp1:getlastmodified>${beforeTime}</lp1:getlastmodified>`
|
||||||
|
+ '</D:prop>'
|
||||||
|
+ '<D:status>HTTP/1.1 200 OK</D:status>'
|
||||||
|
+ '</D:propstat>'
|
||||||
|
+ '</D:response>'
|
||||||
|
+ '<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">'
|
||||||
|
+ `<D:href>${davBasePath}${encodedStrD}/zotero/lastsync</D:href>`
|
||||||
|
+ '<D:propstat>'
|
||||||
|
+ '<D:prop>'
|
||||||
|
+ `<lp1:getlastmodified>${beforeTime}</lp1:getlastmodified>`
|
||||||
|
+ '</D:prop>'
|
||||||
|
+ '<D:status>HTTP/1.1 200 OK</D:status>'
|
||||||
|
+ '</D:propstat>'
|
||||||
|
+ '</D:response>'
|
||||||
|
|
||||||
|
+ '<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">'
|
||||||
|
+ `<D:href>${davBasePath}${encodedStrD}/zotero/AAAAAAAA.zip</D:href>`
|
||||||
|
+ '<D:propstat>'
|
||||||
|
+ '<D:prop>'
|
||||||
|
+ `<lp1:getlastmodified>${beforeTime}</lp1:getlastmodified>`
|
||||||
|
+ '</D:prop>'
|
||||||
|
+ '<D:status>HTTP/1.1 200 OK</D:status>'
|
||||||
|
+ '</D:propstat>'
|
||||||
|
+ '</D:response>'
|
||||||
|
+ '<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">'
|
||||||
|
+ `<D:href>${davBasePath}${encodedStrD}/zotero/AAAAAAAA.prop</D:href>`
|
||||||
|
+ '<D:propstat>'
|
||||||
|
+ '<D:prop>'
|
||||||
|
+ `<lp1:getlastmodified>${beforeTime}</lp1:getlastmodified>`
|
||||||
|
+ '</D:prop>'
|
||||||
|
+ '<D:status>HTTP/1.1 200 OK</D:status>'
|
||||||
|
+ '</D:propstat>'
|
||||||
|
+ '</D:response>'
|
||||||
|
+ '</D:multistatus>'
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.Prefs.set("sync.storage.url", davHostPath + strC + "/");
|
||||||
|
yield controller.purgeOrphanedStorageFiles();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Reference in a new issue