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/. */
/** @type {nsIFile} */
let profileDirectory;
/**
* A tool for creating the files and folders of a profile without starting up
* many of components (e.g. the account manager) that would otherwise be
* necessary. This can be used to test that profile migration happens correctly.
*/
export class ProfileCreator {
/**
* The absolute file system path to this profile.
*
* @type {string}
*/
path;
/**
* @param {nsIFile} directory - Pass `do_get_profile()` to this constructor.
*/
constructor(directory) {
profileDirectory = directory;
this.path = directory.path;
}
/**
* Set up the preferences for an account and server.
* See `nsIMsgAccountManager.createIncomingServer`.
*
* @param {string} username
* @param {string} hostname
* @param {string} type
* @returns {ProfileServer}
*/
addServer(username, hostname, type) {
return new ProfileServer(username, hostname, type);
}
/**
* Set up the preferences for a local account.
*
* @returns {ProfileServer}
*/
addLocalServer() {
return this.addServer("nobody", "Local Folders", "none");
}
/**
* Add a file to the profile.
*
* @param {string} relativePath - The file's path relative to the profile root.
* @param {string} [content=""]
*/
async addFile(relativePath, content = "") {
await IOUtils.writeUTF8(PathUtils.join(this.path, relativePath), content);
}
}
/**
* Represents a server in the profile. In this code, accounts are tied to
* servers for the sake of simplicity.
*/
class ProfileServer {
/** @type {integer} */
static #nextKey = 1;
/** @type {ProfileMailFolder} */
rootFolder;
/** @type {nsIPrefBranch} */
prefBranch;
/**
* Set up the preferences for an account and server.
* See `nsIMsgAccountManager.createIncomingServer`.
*
* @param {string} username
* @param {string} hostname
* @param {string} type
* @returns {ProfileServer}
*/
constructor(username, hostname, type) {
const key = ProfileServer.#nextKey++;
Services.prefs.setStringPref(
`mail.account.account${key}.server`,
`server${key}`
);
let accounts = Services.prefs.getStringPref(
"mail.accountmanager.accounts",
""
);
if (accounts) {
accounts += ",";
}
Services.prefs.setStringPref(
"mail.accountmanager.accounts",
`${accounts}account${key}`
);
this.prefBranch = Services.prefs.getBranch(`mail.server.server${key}.`);
this.prefBranch.setStringPref("userName", username);
this.prefBranch.setStringPref("hostname", hostname);
this.prefBranch.setStringPref("type", type);
const mailFolderName = type == "imap" ? "ImapMail" : "Mail";
this.prefBranch.setStringPref(
"directory-rel",
`[ProfD]${mailFolderName}/${hostname}`
);
const prefsFile = profileDirectory.clone();
prefsFile.append("prefs.js");
Services.prefs.savePrefFile(prefsFile);
this.rootFolder = new ProfileMailFolder(
this,
Services.prefs.getComplexValue(
`mail.server.server${key}.directory-rel`,
Ci.nsIRelativeFilePref
).file.path
);
}
/**
* @type {boolean}
*/
get isMaildirStore() {
return (
this.prefBranch.getStringPref("storeContractID", "") ==
"@mozilla.org/msgstore/maildirstore;1"
);
}
set isMaildirStore(value) {
this.prefBranch.setStringPref(
"storeContractID",
value
? "@mozilla.org/msgstore/maildirstore;1"
: "@mozilla.org/msgstore/berkeleystore;1"
);
}
}
/**
* Represents a mail folder in the profile.
*/
class ProfileMailFolder {
/** @type {ProfileServer} */
server;
/**
* The absolute file system path to the directory (.sbd) for this folder in
* the profile.
*
* @type {string}
*/
path;
/**
* The absolute file system path to the mail summary file (.msf) for this
* folder in the profile.
*
* @type {string}
*/
summaryFilePath;
/**
* The absolute file system path to the mail (mbox file or maildir directory)
* for this folder in the profile.
*
* @type {string}
*/
mailFilePath;
/**
* @param {ProfileServer} server
* @param {string} path - Absolute file system path.
*/
constructor(server, path) {
this.server = server;
this.path = path;
const dirName = PathUtils.parent(path);
const leafName = PathUtils.filename(path).replace(/\.sbd$/, "");
this.summaryFilePath = PathUtils.join(dirName, `${leafName}.msf`);
this.mailFilePath = PathUtils.join(dirName, leafName);
}
/**
* Adds a child folder to this folder.
*
* @param {string} name
* @param {object} files
* @param {nsIFile} [files.summary] - If given, a file to copy into the
* profile as the folder's summary.
* @param {nsIFile} [files.mbox] - If given (and not in a maildir server),
* a file to copy into the profile as the folder's mbox.
* @returns {ProfileMailFolder}
*/
async addMailFolder(name, { summary, mbox } = {}) {
await IOUtils.makeDirectory(this.path);
const folder = new ProfileMailFolder(
this.server,
PathUtils.join(this.path, `${name}.sbd`)
);
if (summary instanceof Ci.nsIFile) {
await IOUtils.copy(summary.path, folder.summaryFilePath);
}
if (this.server.isMaildirStore) {
await IOUtils.makeDirectory(folder.mailFilePath);
} else if (mbox instanceof Ci.nsIFile) {
await IOUtils.copy(mbox.path, folder.mailFilePath);
} else {
await IOUtils.writeUTF8(folder.mailFilePath, "");
}
return folder;
}
}