summaryrefslogtreecommitdiffstats
path: root/comm/calendar/base/modules/utils/calProviderDetectionUtils.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'comm/calendar/base/modules/utils/calProviderDetectionUtils.jsm')
-rw-r--r--comm/calendar/base/modules/utils/calProviderDetectionUtils.jsm182
1 files changed, 182 insertions, 0 deletions
diff --git a/comm/calendar/base/modules/utils/calProviderDetectionUtils.jsm b/comm/calendar/base/modules/utils/calProviderDetectionUtils.jsm
new file mode 100644
index 0000000000..ce76dbdd01
--- /dev/null
+++ b/comm/calendar/base/modules/utils/calProviderDetectionUtils.jsm
@@ -0,0 +1,182 @@
+/* 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/. */
+
+const EXPORTED_SYMBOLS = ["calproviderdetection"];
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+/**
+ * Code to call the calendar provider detection mechanism.
+ */
+
+// NOTE: This module should not be loaded directly, it is available when
+// including calUtils.jsm under the cal.provider.detection namespace.
+
+/**
+ * The base class marker for detection errors. Useful in instanceof checks.
+ */
+class DetectionError extends Error {}
+
+/**
+ * Creates an error class that extends the base detection error.
+ *
+ * @param {string} aName - The name of the constructor, used for the base error class.
+ * @returns {DetectionError} A class extending DetectionError.
+ */
+function DetectionErrorClass(aName) {
+ return class extends DetectionError {
+ constructor(message) {
+ super(message);
+ this.name = aName;
+ }
+ };
+}
+
+/**
+ * The exported `calproviderdetection` object.
+ */
+var calproviderdetection = {
+ /**
+ * A map of providers that implement detection. Maps the type identifier
+ * (e.g. "ics", "caldav") to the provider object.
+ *
+ * @type {Map<string, calICalendarProvider>}
+ */
+ get providers() {
+ let providers = new Map();
+ for (let [type, provider] of cal.provider.providers) {
+ if (provider.detectCalendars) {
+ providers.set(type, provider);
+ }
+ }
+ return providers;
+ },
+
+ /**
+ * Known domains for Google OAuth. This is just to catch the most common case,
+ * MX entries should be checked for remaining cases.
+ *
+ * @type {Set<string>}
+ */
+ googleOAuthDomains: new Set(["gmail.com", "googlemail.com", "apidata.googleusercontent.com"]),
+
+ /**
+ * Translate location and username to an uri. If the location is empty, the
+ * domain part of the username is taken. If the location is a hostname it is
+ * converted to a https:// uri, if it is an uri string then use that.
+ *
+ * @param {string} aLocation - The location string.
+ * @param {string} aUsername - The username string.
+ * @returns {nsIURI} The resulting location uri.
+ */
+ locationToUri(aLocation, aUsername) {
+ let uri = null;
+ if (!aLocation) {
+ let match = aUsername.match(/[^@]+@([^.]+\..*)/);
+ if (match) {
+ uri = Services.io.newURI("https://" + match[1]);
+ }
+ } else if (aLocation.includes("://")) {
+ // Try to parse it as an uri
+ uri = Services.io.newURI(aLocation);
+ } else {
+ // Maybe its just a simple hostname
+ uri = Services.io.newURI("https://" + aLocation);
+ }
+ return uri;
+ },
+
+ /**
+ * Detect calendars using the given information. The location can be a number
+ * of things and handling this is up to the provider. It could be a hostname,
+ * a specific URL, the origin URL, etc.
+ *
+ * @param {string} aUsername - The username for logging in.
+ * @param {string} aPassword - The password for logging in.
+ * @param {string} aLocation - The location information.
+ * @param {boolean} aSavePassword - If true, the credentials will be saved
+ * in the password manager if used.
+ * @param {ProviderFilter[]} aPreDetectFilters - Functions for filtering out providers.
+ * @param {object} aExtraProperties - Extra properties to pass on to the
+ * providers.
+ * @returns {Promise<Map<string, calICalendar[]>>} A promise resolving with a Map of
+ * provider type to calendars found.
+ */
+ async detect(
+ aUsername,
+ aPassword,
+ aLocation,
+ aSavePassword,
+ aPreDetectFilters,
+ aExtraProperties
+ ) {
+ let providers = this.providers;
+
+ if (!providers.size) {
+ throw new calproviderdetection.NoneFoundError(
+ "No providers available that implement calendar detection"
+ );
+ }
+
+ // Filter out the providers that should not be used (for the location, username, etc.).
+ for (let func of aPreDetectFilters) {
+ let typesToFilterOut = func(providers.keys(), aLocation, aUsername);
+ typesToFilterOut.forEach(type => providers.delete(type));
+ }
+
+ let resolutions = await Promise.allSettled(
+ [...providers.values()].map(provider => {
+ let detectionResult = provider.detectCalendars(
+ aUsername,
+ aPassword,
+ aLocation,
+ aSavePassword,
+ aExtraProperties
+ );
+ return detectionResult.then(
+ result => ({ provider, status: Cr.NS_OK, detail: result }),
+ failure => ({ provider, status: Cr.NS_ERROR_FAILURE, detail: failure })
+ );
+ })
+ );
+
+ let failCount = 0;
+ let lastError;
+ let results = new Map(
+ resolutions.reduce((res, resolution) => {
+ let { provider, status, detail } = resolution.value || resolution.reason;
+
+ if (Components.isSuccessCode(status) && detail && detail.length) {
+ res.push([provider, detail]);
+ } else {
+ failCount++;
+ if (detail instanceof DetectionError) {
+ lastError = detail;
+ }
+ }
+
+ return res;
+ }, [])
+ );
+
+ // If everything failed due to one of the detection errors, then pass that on.
+ if (failCount == resolutions.length) {
+ throw lastError || new calproviderdetection.NoneFoundError();
+ }
+
+ return results;
+ },
+
+ /** The base detection error class */
+ Error: DetectionError,
+
+ /** An error that can be thrown if authentication failed */
+ AuthFailedError: DetectionErrorClass("AuthFailedError"),
+
+ /** An error that can be thrown if the location is invalid or has no calendars */
+ NoneFoundError: DetectionErrorClass("NoneFoundError"),
+
+ /** An error that can be thrown if the user canceled the operation */
+ CanceledError: DetectionErrorClass("CanceledError"),
+};