From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../sync/modules/engines/identities.sys.mjs | 394 +++++++++++++++++++++ 1 file changed, 394 insertions(+) create mode 100644 comm/mail/services/sync/modules/engines/identities.sys.mjs (limited to 'comm/mail/services/sync/modules/engines/identities.sys.mjs') diff --git a/comm/mail/services/sync/modules/engines/identities.sys.mjs b/comm/mail/services/sync/modules/engines/identities.sys.mjs new file mode 100644 index 0000000000..07976272eb --- /dev/null +++ b/comm/mail/services/sync/modules/engines/identities.sys.mjs @@ -0,0 +1,394 @@ +/* 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/. */ + +import { CryptoWrapper } from "resource://services-sync/record.sys.mjs"; +import { + Store, + SyncEngine, + Tracker, +} from "resource://services-sync/engines.sys.mjs"; +import { Utils } from "resource://services-sync/util.sys.mjs"; + +const { SCORE_INCREMENT_XLARGE } = ChromeUtils.import( + "resource://services-sync/constants.js" +); +const { MailServices } = ChromeUtils.import( + "resource:///modules/MailServices.jsm" +); + +const SYNCED_IDENTITY_PROPERTIES = { + attachSignature: "attach_signature", + attachVCard: "attach_vcard", + autoQuote: "auto_quote", + catchAll: "catchAll", + catchAllHint: "catchAllHint", + composeHtml: "compose_html", + email: "useremail", + escapedVCard: "escapedVCard", + fullName: "fullName", + htmlSigFormat: "htmlSigFormat", + htmlSigText: "htmlSigText", + label: "label", + organization: "organization", + replyOnTop: "reply_on_top", + replyTo: "reply_to", + sigBottom: "sig_bottom", + sigOnForward: "sig_on_fwd", + sigOnReply: "sig_on_reply", +}; + +/** + * IdentityRecord represents the state of an add-on in an application. + * + * Each add-on has its own record for each application ID it is installed + * on. + * + * The ID of add-on records is a randomly-generated GUID. It is random instead + * of deterministic so the URIs of the records cannot be guessed and so + * compromised server credentials won't result in disclosure of the specific + * add-ons present in a Sync account. + * + * The record contains the following fields: + * + */ +export function IdentityRecord(collection, id) { + CryptoWrapper.call(this, collection, id); +} + +IdentityRecord.prototype = { + __proto__: CryptoWrapper.prototype, + _logName: "Record.Identity", +}; +Utils.deferGetSet(IdentityRecord, "cleartext", ["accounts", "prefs", "smtpID"]); + +export function IdentitiesEngine(service) { + SyncEngine.call(this, "Identities", service); +} + +IdentitiesEngine.prototype = { + __proto__: SyncEngine.prototype, + _storeObj: IdentityStore, + _trackerObj: IdentityTracker, + _recordObj: IdentityRecord, + version: 1, + syncPriority: 4, + + /* + * Returns a changeset for this sync. Engine implementations can override this + * method to bypass the tracker for certain or all changed items. + */ + async getChangedIDs() { + return this._tracker.getChangedIDs(); + }, +}; + +function IdentityStore(name, engine) { + Store.call(this, name, engine); +} +IdentityStore.prototype = { + __proto__: Store.prototype, + + /** + * Create an item in the store from a record. + * + * This is called by the default implementation of applyIncoming(). If using + * applyIncomingBatch(), this won't be called unless your store calls it. + * + * @param record + * The store record to create an item from + */ + async create(record) { + let identity = MailServices.accounts.createIdentity(); + identity.UID = record.id; + + for (let key of Object.keys(SYNCED_IDENTITY_PROPERTIES)) { + if (key in record.prefs) { + identity[key] = record.prefs[key]; + } + } + + if (record.smtpID) { + let smtpServer = MailServices.smtp.servers.find( + s => s.UID == record.smtpID + ); + if (smtpServer) { + identity.smtpServerKey = smtpServer.key; + } else { + this._log.warn( + `Identity uses SMTP server ${record.smtpID}, but it doesn't exist.` + ); + } + } + + for (let { id, isDefault } of record.accounts) { + let account = MailServices.accounts.accounts.find( + a => a.incomingServer?.UID == id + ); + if (account) { + account.addIdentity(identity); + if (isDefault) { + account.defaultIdentity = identity; + } + } else { + this._log.warn(`Identity is for account ${id}, but it doesn't exist.`); + } + } + }, + + /** + * Remove an item in the store from a record. + * + * This is called by the default implementation of applyIncoming(). If using + * applyIncomingBatch(), this won't be called unless your store calls it. + * + * @param record + * The store record to delete an item from + */ + async remove(record) { + let identity = MailServices.accounts.allIdentities.find( + i => i.UID == record.id + ); + if (!identity) { + this._log.trace("Asked to remove record that doesn't exist, ignoring"); + return; + } + + for (let server of MailServices.accounts.getServersForIdentity(identity)) { + let account = MailServices.accounts.FindAccountForServer(server); + account.removeIdentity(identity); + // Removing the identity from one account should destroy it. + // No need to continue. + return; + } + }, + + /** + * Update an item from a record. + * + * This is called by the default implementation of applyIncoming(). If using + * applyIncomingBatch(), this won't be called unless your store calls it. + * + * @param record + * The record to use to update an item from + */ + async update(record) { + let identity = MailServices.accounts.allIdentities.find( + i => i.UID == record.id + ); + if (!identity) { + this._log.trace("Skipping update for unknown item: " + record.id); + return; + } + + for (let key of Object.keys(SYNCED_IDENTITY_PROPERTIES)) { + if (key in record.prefs) { + identity[key] = record.prefs[key]; + } + } + + if (record.smtpID) { + let smtpServer = MailServices.smtp.servers.find( + s => s.UID == record.smtpID + ); + if (smtpServer) { + identity.smtpServerKey = smtpServer.key; + } else { + this._log.warn( + `Identity uses SMTP server ${record.smtpID}, but it doesn't exist.` + ); + } + } else { + identity.smtpServerKey = null; + } + + for (let { id, isDefault } of record.accounts) { + let account = MailServices.accounts.accounts.find( + a => a.incomingServer?.UID == id + ); + if (account) { + if (!account.identities.includes(identity)) { + account.addIdentity(identity); + } + if (isDefault && account.defaultIdentity != identity) { + account.defaultIdentity = identity; + } + } else { + this._log.warn(`Identity is for account ${id}, but it doesn't exist.`); + } + } + }, + + /** + * Determine whether a record with the specified ID exists. + * + * Takes a string record ID and returns a booleans saying whether the record + * exists. + * + * @param id + * string record ID + * @return boolean indicating whether record exists locally + */ + async itemExists(id) { + return id in (await this.getAllIDs()); + }, + + /** + * Obtain the set of all known record IDs. + * + * @return Object with ID strings as keys and values of true. The values + * are ignored. + */ + async getAllIDs() { + let ids = {}; + for (let i of MailServices.accounts.allIdentities) { + let servers = MailServices.accounts.getServersForIdentity(i); + if (servers.find(s => ["imap", "pop3"].includes(s.type))) { + ids[i.UID] = true; + } + } + return ids; + }, + + /** + * Create a record from the specified ID. + * + * If the ID is known, the record should be populated with metadata from + * the store. If the ID is not known, the record should be created with the + * delete field set to true. + * + * @param id + * string record ID + * @param collection + * Collection to add record to. This is typically passed into the + * constructor for the newly-created record. + * @return record type for this engine + */ + async createRecord(id, collection) { + let record = new IdentityRecord(collection, id); + + let identity = MailServices.accounts.allIdentities.find(i => i.UID == id); + + // If we don't know about this ID, mark the record as deleted. + if (!identity) { + record.deleted = true; + return record; + } + + record.accounts = []; + for (let server of MailServices.accounts.getServersForIdentity(identity)) { + let account = MailServices.accounts.FindAccountForServer(server); + if (account) { + record.accounts.push({ + id: server.UID, + isDefault: account.defaultIdentity == identity, + }); + } + } + + record.prefs = {}; + for (let key of Object.keys(SYNCED_IDENTITY_PROPERTIES)) { + record.prefs[key] = identity[key]; + } + + if (identity.smtpServerKey) { + let smtpServer = MailServices.smtp.getServerByIdentity(identity); + record.smtpID = smtpServer.UID; + } + + return record; + }, +}; + +function IdentityTracker(name, engine) { + Tracker.call(this, name, engine); +} +IdentityTracker.prototype = { + __proto__: Tracker.prototype, + + _changedIDs: new Set(), + _ignoreAll: false, + + async getChangedIDs() { + let changes = {}; + for (let id of this._changedIDs) { + changes[id] = 0; + } + return changes; + }, + + clearChangedIDs() { + this._changedIDs.clear(); + }, + + get ignoreAll() { + return this._ignoreAll; + }, + + set ignoreAll(value) { + this._ignoreAll = value; + }, + + onStart() { + Services.prefs.addObserver("mail.identity.", this); + Services.obs.addObserver(this, "account-identity-added"); + Services.obs.addObserver(this, "account-identity-removed"); + Services.obs.addObserver(this, "account-default-identity-changed"); + }, + + onStop() { + Services.prefs.removeObserver("mail.account.", this); + Services.obs.removeObserver(this, "account-identity-added"); + Services.obs.removeObserver(this, "account-identity-removed"); + Services.obs.removeObserver(this, "account-default-identity-changed"); + }, + + observe(subject, topic, data) { + if (this._ignoreAll) { + return; + } + + let markAsChanged = identity => { + if (identity && !this._changedIDs.has(identity.UID)) { + this._changedIDs.add(identity.UID); + this.score = SCORE_INCREMENT_XLARGE; + } + }; + + if ( + ["account-identity-added", "account-identity-removed"].includes(topic) + ) { + markAsChanged(subject.QueryInterface(Ci.nsIMsgIdentity)); + return; + } + + if (topic == "account-default-identity-changed") { + // The default identity has changed, update the default identity and + // the previous one, which will now be second on the list. + let [newDefault, oldDefault] = Services.prefs + .getStringPref(`mail.account.${data}.identities`) + .split(","); + if (newDefault) { + markAsChanged(MailServices.accounts.getIdentity(newDefault)); + } + if (oldDefault) { + markAsChanged(MailServices.accounts.getIdentity(oldDefault)); + } + return; + } + + let idKey = data.split(".")[2]; + let prefName = data.substring(idKey.length + 15); + if ( + prefName != "smtpServer" && + !Object.values(SYNCED_IDENTITY_PROPERTIES).includes(prefName) + ) { + return; + } + + // Don't use .getIdentity because it will create one if it doesn't exist. + markAsChanged( + MailServices.accounts.allIdentities.find(i => i.key == idKey) + ); + }, +}; -- cgit v1.2.3