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
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
});
const OUTGOING_CONTRACT_ID_PREFIX =
  "@mozilla.org/messenger/outgoing/server;1?type=";
/**
 * The service in charge of creating and referencing all known outgoing message
 * servers.
 *
 * @implements {nsIMsgOutgoingServerService}
 */
export class OutgoingServerService {
  QueryInterface = ChromeUtils.generateQI(["nsIMsgOutgoingServerService"]);
  constructor() {
    this._servers = [];
    this._logger = lazy.MsgUtils.smtpLogger;
  }
  /**
   * @see nsIMsgOutgoingServerService
   */
  get defaultServer() {
    const defaultServerKey = Services.prefs.getCharPref(
      "mail.smtp.defaultserver",
      ""
    );
    if (defaultServerKey) {
      // Get it from the prefs.
      return this.getServerByKey(defaultServerKey);
    }
    // No pref set, so set the first one as default, and return it.
    if (this.servers.length > 0) {
      this.defaultServer = this.servers[0];
      return this.servers[0];
    }
    return null;
  }
  set defaultServer(server) {
    Services.prefs.setCharPref("mail.smtp.defaultserver", server.key);
  }
  get servers() {
    if (!this._servers.length) {
      // Load outgoing servers from prefs.
      this._servers = this._getServerKeys().map(key => this._keyToServer(key));
    }
    return this._servers;
  }
  /**
   * @see nsIMsgOutgoingServerService
   */
  getServerByIdentity(userIdentity) {
    return userIdentity.smtpServerKey
      ? this.getServerByKey(userIdentity.smtpServerKey)
      : this.defaultServer;
  }
  /**
   * @see nsIMsgOutgoingServerService
   */
  getServerByKey(key) {
    return this.servers.find(s => s.key == key);
  }
  /**
   * @see nsIMsgOutgoingServerService
   */
  createServer(type) {
    if (!(OUTGOING_CONTRACT_ID_PREFIX + type in Cc)) {
      // The error thrown when the contract ID is unknown can be a bit cryptic,
      // and can seem to come from an unusual place (the getter for this.servers),
      // so throw something a bit more explicit.
      throw Components.Exception(
        `tried to create an outgoing server with unknown type ${type}`,
        Cr.NS_ERROR_INVALID_ARG
      );
    }
    const serverKeys = this._getServerKeys();
    let i = 1;
    let key;
    do {
      key = `${type}${i++}`;
    } while (serverKeys.includes(key));
    serverKeys.push(key);
    // Set the server's type, which defines which implementation to instantiate.
    Services.prefs.setCharPref(`mail.smtpserver.${key}.type`, type);
    this._saveServerKeys(serverKeys);
    this._servers.push(this._keyToServer(key));
    return this.servers.at(-1);
  }
  /**
   * @see nsIMsgOutgoingServerService
   */
  deleteServer(server) {
    const serverKeys = this._getServerKeys().filter(k => k != server.key);
    this._servers = this.servers.filter(s => s.key != server.key);
    this._saveServerKeys(serverKeys);
    Services.obs.notifyObservers(
      server,
      "message-smtpserver-removed",
      server.key
    );
  }
  /**
   * @see nsIMsgOutgoingServerService
   */
  findServer(username, hostname, type) {
    username = username?.toLowerCase();
    // Note: we don't need to wrap the username in square brackets if it's an
    // IPv6, because nsIURI.host does not include brackets.
    hostname = hostname?.toLowerCase();
    type = type?.toLowerCase();
    return this.servers.find(server => {
      if (
        (username && server.username.toLowerCase() != username) ||
        (hostname && server.serverURI.host.toLowerCase() != hostname) ||
        (type && server.type.toLowerCase() != type)
      ) {
        return false;
      }
      return true;
    });
  }
  /**
   * Get all outgoing server keys from prefs.
   *
   * @returns {string[]}
   */
  _getServerKeys() {
    return Services.prefs
      .getCharPref("mail.smtpservers", "")
      .split(",")
      .filter(Boolean);
  }
  /**
   * Save outgoing server keys to prefs.
   *
   * @param {string[]} keys - The key list to save.
   */
  _saveServerKeys(keys) {
    return Services.prefs.setCharPref("mail.smtpservers", keys.join(","));
  }
  /**
   * Create an nsIMsgOutgoingServer from a key.
   *
   * @param {string} key - The key for the outgoing server.
   * @returns {nsIMsgOutgoingServer}
   */
  _keyToServer(key) {
    // Ideally we should be failing early if we can't figure out the type,
    // because we might be trying to configure the server for the wrong
    // protocol. However, We might be currently migrating an old profile that
    // predates this pref being introduced, in which case we'll try to read this
    // pref before the profile migration code has had a chance to set it. In
    // which case, it's likely safe to assume the server's type is SMTP.
    const serverType = Services.prefs.getCharPref(
      `mail.smtpserver.${key}.type`,
      "smtp"
    );
    const server = Cc[OUTGOING_CONTRACT_ID_PREFIX + serverType].createInstance(
      Ci.nsIMsgOutgoingServer
    );
    // Setting the server key will set up all of its other properties by
    // reading them from the prefs.
    server.key = key;
    return server;
  }
}