Revision control

Copy as Markdown

Other Tools

/* 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/. */
// Tests that hrefs pointing to a different origin than the address book's
// server are never requested. Only their path is used, on the address book's
// server.
const { HttpServer } = ChromeUtils.importESModule(
);
const otherServer = new HttpServer();
const otherRequests = [];
add_setup(function () {
otherServer.registerPrefixHandler("/", (request, response) => {
otherRequests.push(`${request.method} ${request.path}`);
response.setStatusLine("1.1", 204, "No Content");
});
otherServer.start(-1);
registerCleanupFunction(
() => new Promise(resolve => otherServer.stop(resolve))
);
});
function otherURL(path) {
return `http://localhost:${otherServer.identity.primaryPort}${path}`;
}
function putCardAt(href, uid) {
const vCard = `BEGIN:VCARD\r\nUID:${uid}\r\nFN:${uid}\r\nEND:VCARD\r\n`;
CardDAVServer.cards.set(href, {
etag: "" + vCard.length,
vCard,
changed: ++CardDAVServer.changeCount,
});
}
add_task(async function foreignHrefIsReducedToPath() {
// The server reports this card as being on another origin, as a server
// behind a proxy might.
const elsewhere = `${CardDAVServer.path}elsewhere.vcf`;
const originalCardResponse = CardDAVServer._cardResponse;
CardDAVServer._cardResponse = function (href, ...args) {
return originalCardResponse.call(
this,
href == elsewhere ? otherURL(href) : href,
...args
);
};
CardDAVServer.putCardInternal(
"elsewhere.vcf",
"BEGIN:VCARD\r\nUID:elsewhere\r\nFN:Elsewhere\r\nEND:VCARD\r\n"
);
putCardAt(`${CardDAVServer.url}absolute.vcf`, "absolute");
const directory = await initDirectory();
await directory.fetchAllFromServer();
Assert.deepEqual(
directory.childCards.map(c => c.UID).sort(),
["absolute", "elsewhere"],
"all cards should be on the client"
);
Assert.equal(
directory.getCard("elsewhere").getProperty("_href", ""),
elsewhere,
"only the path of the foreign href should be stored"
);
Assert.equal(
directory.getCard("absolute").getProperty("_href", ""),
`${CardDAVServer.url}absolute.vcf`,
"an href on the server's origin should be stored as it is"
);
info("Changing the card on the server, then syncing.");
CardDAVServer.putCardInternal(
"elsewhere.vcf",
"BEGIN:VCARD\r\nUID:elsewhere\r\nFN:Changed on server\r\nEND:VCARD\r\n"
);
await directory.syncWithServer();
Assert.equal(
directory.getCard("elsewhere").displayName,
"Changed on server",
"the change on the server should reach the client"
);
info("Changing the card on the client.");
const card = directory.getCard("elsewhere");
card.vCardProperties.clearValues("fn");
card.vCardProperties.addValue("fn", "Changed on client");
directory.modifyCard(card);
await TestUtils.waitForCondition(
() =>
CardDAVServer.cards.get(elsewhere).vCard.includes("Changed on client"),
"the change should reach the address book's server"
);
await Assert.rejects(
directory._makeRequest(otherURL(elsewhere), { method: "DELETE" }),
/different server/,
"a request to a different origin should be refused"
);
Assert.deepEqual(
otherRequests,
[],
"no request should reach the other server"
);
CardDAVServer._cardResponse = originalCardResponse;
await clearDirectory(directory);
CardDAVServer.reset();
});
add_task(async function queuedForeignHrefIsRemovedFromServer() {
CardDAVServer.putCardInternal(
"keep-me.vcf",
"BEGIN:VCARD\r\nUID:keep-me\r\nFN:I shall stay.\r\nEND:VCARD\r\n"
);
CardDAVServer.putCardInternal(
"gone.vcf",
"BEGIN:VCARD\r\nUID:gone\r\nFN:I shall go.\r\nEND:VCARD\r\n"
);
// A foreign href stored by an earlier version is waiting to be removed.
Services.prefs.setStringPref(
"ldap_2.servers.carddav.carddav.hrefsToRemove",
otherURL(`${CardDAVServer.path}gone.vcf`)
);
const directory = await initDirectory();
await directory.fetchAllFromServer();
info("Adding a card on the server, then syncing.");
CardDAVServer.putCardInternal(
"sync-me.vcf",
"BEGIN:VCARD\r\nUID:sync-me\r\nFN:Please sync me.\r\nEND:VCARD\r\n"
);
await directory.syncWithServer();
Assert.deepEqual(
directory.childCards.map(c => c.UID).sort(),
["keep-me", "sync-me"],
"the card should be removed, and the sync should continue"
);
Assert.ok(
!CardDAVServer.cards.has(`${CardDAVServer.path}gone.vcf`),
"the card should be removed from the address book's server"
);
Assert.equal(
directory._hrefsToRemove.size,
0,
"the foreign href should no longer be waiting to be removed"
);
Assert.deepEqual(
otherRequests,
[],
"no request should reach the other server"
);
await clearDirectory(directory);
CardDAVServer.reset();
});
add_task(async function foreignHrefOnLocalCardIsReducedToPath() {
const directory = await initDirectory();
await directory.fetchAllFromServer();
info("Adding a card with a foreign href stored by an earlier version.");
const card = Cc["@mozilla.org/addressbook/cardproperty;1"].createInstance(
Ci.nsIAbCard
);
card.UID = "local";
card.displayName = "Local";
card.setProperty("_href", otherURL(`${CardDAVServer.path}local.vcf`));
const updated = TestUtils.topicObserved("addrbook-contact-updated");
directory.addCard(card);
await updated;
Assert.ok(
CardDAVServer.cards.has(`${CardDAVServer.path}local.vcf`),
"the card should be sent to the address book's server"
);
Assert.equal(
directory.getCard("local").getProperty("_href", ""),
`${CardDAVServer.path}local.vcf`,
"the card should have the href on the address book's server"
);
Assert.deepEqual(
otherRequests,
[],
"no request should reach the other server"
);
await clearDirectory(directory);
CardDAVServer.reset();
});
/**
* Stores a foreign href for a card, as an earlier version would have.
*
* @param {CardDAVDirectory} directory
* @param {string} uid
* @param {string} href
*/
function storeForeignHref(directory, uid, href) {
const card = directory.getCard(uid);
card.setProperty("_href", href);
Object.getPrototypeOf(CardDAVDirectory.prototype).modifyCard.call(
directory,
card
);
}
function clearNotifications() {
for (const array of Object.values(observer.notifications)) {
array.length = 0;
}
}
add_task(async function storedForeignHrefMatchesOnSync() {
observer.init();
CardDAVServer.putCardInternal(
"legacy.vcf",
"BEGIN:VCARD\r\nUID:legacy\r\nFN:Legacy\r\nEND:VCARD\r\n"
);
const directory = await initDirectory();
await directory.fetchAllFromServer();
storeForeignHref(
directory,
"legacy",
otherURL(`${CardDAVServer.path}legacy.vcf`)
);
clearNotifications();
info("Fetching everything from the server.");
await directory.fetchAllFromServer();
observer.checkAndClearNotifications({
"addrbook-contact-created": [],
"addrbook-contact-updated": [],
"addrbook-contact-deleted": [],
});
info("Syncing without a sync token.");
CardDAVServer.mimicYahoo = true;
directory._syncToken = "";
await directory.syncWithServer();
CardDAVServer.mimicYahoo = false;
observer.checkAndClearNotifications({
"addrbook-contact-created": [],
"addrbook-contact-updated": [],
"addrbook-contact-deleted": [],
});
Assert.deepEqual(
otherRequests,
[],
"no request should reach the other server"
);
await clearDirectory(directory);
CardDAVServer.reset();
});
add_task(async function storedForeignHrefIsDeletedOnSync() {
observer.init();
CardDAVServer.putCardInternal(
"legacy.vcf",
"BEGIN:VCARD\r\nUID:legacy\r\nFN:Legacy\r\nEND:VCARD\r\n"
);
const directory = await initDirectory();
await directory.fetchAllFromServer();
Assert.ok(directory._syncToken, "the directory should have a sync token");
storeForeignHref(
directory,
"legacy",
otherURL(`${CardDAVServer.path}legacy.vcf`)
);
clearNotifications();
info("Deleting the card on the server, then syncing.");
CardDAVServer.deleteCardInternal("legacy.vcf");
await directory.syncWithServer();
observer.checkAndClearNotifications({
"addrbook-contact-created": [],
"addrbook-contact-updated": [],
"addrbook-contact-deleted": ["legacy"],
});
Assert.deepEqual(
directory.childCards,
[],
"the card deleted on the server should be deleted on the client"
);
Assert.deepEqual(
otherRequests,
[],
"no request should reach the other server"
);
await clearDirectory(directory);
CardDAVServer.reset();
});