summaryrefslogtreecommitdiffstats
path: root/mailnews/base/prefs/content/accountcreation/fetchConfig.js
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/base/prefs/content/accountcreation/fetchConfig.js')
-rw-r--r--mailnews/base/prefs/content/accountcreation/fetchConfig.js240
1 files changed, 240 insertions, 0 deletions
diff --git a/mailnews/base/prefs/content/accountcreation/fetchConfig.js b/mailnews/base/prefs/content/accountcreation/fetchConfig.js
new file mode 100644
index 000000000..07a9f5586
--- /dev/null
+++ b/mailnews/base/prefs/content/accountcreation/fetchConfig.js
@@ -0,0 +1,240 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * Tries to find a configuration for this ISP on the local harddisk, in the
+ * application install directory's "isp" subdirectory.
+ * Params @see fetchConfigFromISP()
+ */
+
+Components.utils.import("resource:///modules/mailServices.js");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/JXON.js");
+
+
+function fetchConfigFromDisk(domain, successCallback, errorCallback)
+{
+ return new TimeoutAbortable(runAsync(function()
+ {
+ try {
+ // <TB installdir>/isp/example.com.xml
+ var configLocation = Services.dirsvc.get("CurProcD", Ci.nsIFile);
+ configLocation.append("isp");
+ configLocation.append(sanitize.hostname(domain) + ".xml");
+
+ var contents =
+ readURLasUTF8(Services.io.newFileURI(configLocation));
+ let domParser = Cc["@mozilla.org/xmlextras/domparser;1"]
+ .createInstance(Ci.nsIDOMParser);
+ successCallback(readFromXML(JXON.build(
+ domParser.parseFromString(contents, "text/xml"))));
+ } catch (e) { errorCallback(e); }
+ }));
+}
+
+/**
+ * Tries to get a configuration from the ISP / mail provider directly.
+ *
+ * Disclaimers:
+ * - To support domain hosters, we cannot use SSL. That means we
+ * rely on insecure DNS and http, which means the results may be
+ * forged when under attack. The same is true for guessConfig(), though.
+ *
+ * @param domain {String} The domain part of the user's email address
+ * @param emailAddress {String} The user's email address
+ * @param successCallback {Function(config {AccountConfig}})} A callback that
+ * will be called when we could retrieve a configuration.
+ * The AccountConfig object will be passed in as first parameter.
+ * @param errorCallback {Function(ex)} A callback that
+ * will be called when we could not retrieve a configuration,
+ * for whatever reason. This is expected (e.g. when there's no config
+ * for this domain at this location),
+ * so do not unconditionally show this to the user.
+ * The first paramter will be an exception object or error string.
+ */
+function fetchConfigFromISP(domain, emailAddress, successCallback,
+ errorCallback)
+{
+ if (!Services.prefs.getBoolPref(
+ "mailnews.auto_config.fetchFromISP.enabled")) {
+ errorCallback("ISP fetch disabled per user preference");
+ return;
+ }
+
+ let url1 = "http://autoconfig." + sanitize.hostname(domain) +
+ "/mail/config-v1.1.xml";
+ // .well-known/ <http://tools.ietf.org/html/draft-nottingham-site-meta-04>
+ let url2 = "http://" + sanitize.hostname(domain) +
+ "/.well-known/autoconfig/mail/config-v1.1.xml";
+ let sucAbortable = new SuccessiveAbortable();
+ var time = Date.now();
+ var urlArgs = { emailaddress: emailAddress };
+ if (!Services.prefs.getBoolPref(
+ "mailnews.auto_config.fetchFromISP.sendEmailAddress")) {
+ delete urlArgs.emailaddress;
+ }
+ let fetch1 = new FetchHTTP(url1, urlArgs, false,
+ function(result)
+ {
+ successCallback(readFromXML(result));
+ },
+ function(e1) // fetch1 failed
+ {
+ ddump("fetchisp 1 <" + url1 + "> took " + (Date.now() - time) +
+ "ms and failed with " + e1);
+ time = Date.now();
+ if (e1 instanceof CancelledException)
+ {
+ errorCallback(e1);
+ return;
+ }
+
+ let fetch2 = new FetchHTTP(url2, urlArgs, false,
+ function(result)
+ {
+ successCallback(readFromXML(result));
+ },
+ function(e2)
+ {
+ ddump("fetchisp 2 <" + url2 + "> took " + (Date.now() - time) +
+ "ms and failed with " + e2);
+ // return the error for the primary call,
+ // unless the fetch was cancelled
+ errorCallback(e2 instanceof CancelledException ? e2 : e1);
+ });
+ sucAbortable.current = fetch2;
+ fetch2.start();
+ });
+ sucAbortable.current = fetch1;
+ fetch1.start();
+ return sucAbortable;
+}
+
+/**
+ * Tries to get a configuration for this ISP from a central database at
+ * Mozilla servers.
+ * Params @see fetchConfigFromISP()
+ */
+
+function fetchConfigFromDB(domain, successCallback, errorCallback)
+{
+ let url = Services.prefs.getCharPref("mailnews.auto_config_url");
+ domain = sanitize.hostname(domain);
+
+ // If we don't specify a place to put the domain, put it at the end.
+ if (!url.includes("{{domain}}"))
+ url = url + domain;
+ else
+ url = url.replace("{{domain}}", domain);
+ url = url.replace("{{accounts}}", MailServices.accounts.accounts.length);
+
+ if (!url.length)
+ return errorCallback("no fetch url set");
+ let fetch = new FetchHTTP(url, null, false,
+ function(result)
+ {
+ successCallback(readFromXML(result));
+ },
+ errorCallback);
+ fetch.start();
+ return fetch;
+}
+
+/**
+ * Does a lookup of DNS MX, to get the server who is responsible for
+ * recieving mail for this domain. Then it takes the domain of that
+ * server, and does another lookup (in ISPDB and possible at ISP autoconfig
+ * server) and if such a config is found, returns that.
+ *
+ * Disclaimers:
+ * - DNS is unprotected, meaning the results could be forged.
+ * The same is true for fetchConfigFromISP() and guessConfig(), though.
+ * - DNS MX tells us the incoming server, not the mailbox (IMAP) server.
+ * They are different. This mechnism is only an approximation
+ * for hosted domains (yourname.com is served by mx.hoster.com and
+ * therefore imap.hoster.com - that "therefore" is exactly the
+ * conclusional jump we make here.) and alternative domains
+ * (e.g. yahoo.de -> yahoo.com).
+ * - We make a look up for the base domain. E.g. if MX is
+ * mx1.incoming.servers.hoster.com, we look up hoster.com.
+ * Thanks to Services.eTLD, we also get bbc.co.uk right.
+ *
+ * Params @see fetchConfigFromISP()
+ */
+function fetchConfigForMX(domain, successCallback, errorCallback)
+{
+ domain = sanitize.hostname(domain);
+
+ var sucAbortable = new SuccessiveAbortable();
+ var time = Date.now();
+ sucAbortable.current = getMX(domain,
+ function(mxHostname) // success
+ {
+ ddump("getmx took " + (Date.now() - time) + "ms");
+ let sld = Services.eTLD.getBaseDomainFromHost(mxHostname);
+ ddump("base domain " + sld + " for " + mxHostname);
+ if (sld == domain)
+ {
+ errorCallback("MX lookup would be no different from domain");
+ return;
+ }
+ sucAbortable.current = fetchConfigFromDB(sld, successCallback,
+ errorCallback);
+ },
+ errorCallback);
+ return sucAbortable;
+}
+
+/**
+ * Queries the DNS MX for the domain
+ *
+ * The current implementation goes to a web service to do the
+ * DNS resolve for us, because Mozilla unfortunately has no implementation
+ * to do it. That's just a workaround. Once bug 545866 is fixed, we make
+ * the DNS query directly on the client. The API of this function should not
+ * change then.
+ *
+ * Returns (in successCallback) the hostname of the MX server.
+ * If there are several entires with different preference values,
+ * only the most preferred (i.e. those with the lowest value)
+ * is returned. If there are several most preferred servers (i.e.
+ * round robin), only one of them is returned.
+ *
+ * @param domain @see fetchConfigFromISP()
+ * @param successCallback {function(hostname {String})
+ * Called when we found an MX for the domain.
+ * For |hostname|, see description above.
+ * @param errorCallback @see fetchConfigFromISP()
+ * @returns @see fetchConfigFromISP()
+ */
+function getMX(domain, successCallback, errorCallback)
+{
+ domain = sanitize.hostname(domain);
+
+ let url = Services.prefs.getCharPref("mailnews.mx_service_url");
+ if (!url)
+ errorCallback("no URL for MX service configured");
+ url += domain;
+
+ let fetch = new FetchHTTP(url, null, false,
+ function(result)
+ {
+ // result is plain text, with one line per server.
+ // So just take the first line
+ ddump("MX query result: \n" + result + "(end)");
+ assert(typeof(result) == "string");
+ let first = result.split("\n")[0];
+ first.toLowerCase().replace(/[^a-z0-9\-_\.]*/g, "");
+ if (first.length == 0)
+ {
+ errorCallback("no MX found");
+ return;
+ }
+ successCallback(first);
+ },
+ errorCallback);
+ fetch.start();
+ return fetch;
+}