summaryrefslogtreecommitdiffstats
path: root/extensions/pref/autoconfig/src/nsAutoConfig.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/pref/autoconfig/src/nsAutoConfig.cpp')
-rw-r--r--extensions/pref/autoconfig/src/nsAutoConfig.cpp463
1 files changed, 463 insertions, 0 deletions
diff --git a/extensions/pref/autoconfig/src/nsAutoConfig.cpp b/extensions/pref/autoconfig/src/nsAutoConfig.cpp
new file mode 100644
index 0000000000..d841512c93
--- /dev/null
+++ b/extensions/pref/autoconfig/src/nsAutoConfig.cpp
@@ -0,0 +1,463 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#include "mozilla/ResultExtensions.h"
+#include "nsAutoConfig.h"
+#include "nsJSConfigTriggers.h"
+
+#include "nsIURI.h"
+#include "nsIHttpChannel.h"
+#include "nsThreadUtils.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIObserverService.h"
+#include "nsLiteralString.h"
+#include "nsIPromptService.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsIStringBundle.h"
+#include "nsContentUtils.h"
+#include "nsCRT.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nspr.h"
+#include <algorithm>
+
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Logging.h"
+#include "mozilla/SpinEventLoopUntil.h"
+
+using mozilla::LogLevel;
+
+mozilla::LazyLogModule MCD("MCD");
+
+// nsISupports Implementation
+
+NS_IMPL_ISUPPORTS(nsAutoConfig, nsITimerCallback, nsIStreamListener,
+ nsIObserver, nsIRequestObserver, nsISupportsWeakReference,
+ nsINamed)
+
+nsAutoConfig::nsAutoConfig() {}
+
+nsresult nsAutoConfig::Init() {
+ // member initializers and constructor code
+
+ nsresult rv;
+ mLoaded = false;
+
+ // Registering the object as an observer to the profile-after-change topic
+ nsCOMPtr<nsIObserverService> observerService =
+ do_GetService("@mozilla.org/observer-service;1", &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = observerService->AddObserver(this, "profile-after-change", true);
+
+ return rv;
+}
+
+nsAutoConfig::~nsAutoConfig() {}
+
+void nsAutoConfig::SetConfigURL(const char* aConfigURL) {
+ mConfigURL.Assign(aConfigURL);
+}
+
+NS_IMETHODIMP
+nsAutoConfig::OnStartRequest(nsIRequest* request) { return NS_OK; }
+
+NS_IMETHODIMP
+nsAutoConfig::OnDataAvailable(nsIRequest* request, nsIInputStream* aIStream,
+ uint64_t aSourceOffset, uint32_t aLength) {
+ uint32_t amt, size;
+ nsresult rv;
+ char buf[1024];
+
+ while (aLength) {
+ size = std::min<size_t>(aLength, sizeof(buf));
+ rv = aIStream->Read(buf, size, &amt);
+ if (NS_FAILED(rv)) return rv;
+ mBuf.Append(buf, amt);
+ aLength -= amt;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAutoConfig::OnStopRequest(nsIRequest* request, nsresult aStatus) {
+ nsresult rv;
+
+ // If the request is failed, go read the failover.jsc file
+ if (NS_FAILED(aStatus)) {
+ MOZ_LOG(MCD, LogLevel::Debug,
+ ("mcd request failed with status %" PRIx32 "\n",
+ static_cast<uint32_t>(aStatus)));
+ return readOfflineFile();
+ }
+
+ // Checking for the http response, if failure go read the failover file.
+ nsCOMPtr<nsIHttpChannel> pHTTPCon(do_QueryInterface(request));
+ if (pHTTPCon) {
+ uint32_t httpStatus;
+ rv = pHTTPCon->GetResponseStatus(&httpStatus);
+ if (NS_FAILED(rv) || httpStatus != 200) {
+ MOZ_LOG(MCD, LogLevel::Debug,
+ ("mcd http request failed with status %x\n", httpStatus));
+ return readOfflineFile();
+ }
+ }
+
+ // Send the autoconfig.jsc to javascript engine.
+
+ rv = EvaluateAdminConfigScript(mBuf.get(), mBuf.Length(), nullptr, false,
+ true, false);
+ if (NS_SUCCEEDED(rv)) {
+ // Write the autoconfig.jsc to failover.jsc (cached copy)
+ rv = writeFailoverFile();
+
+ if (NS_FAILED(rv)) NS_WARNING("Error writing failover.jsc file");
+
+ // Releasing the lock to allow the main thread to start execution
+ mLoaded = true;
+
+ return NS_OK;
+ }
+ // there is an error in parsing of the autoconfig file.
+ NS_WARNING(
+ "Error reading autoconfig.jsc from the network, reading the offline "
+ "version");
+ return readOfflineFile();
+}
+
+// Notify method as a TimerCallBack function
+NS_IMETHODIMP nsAutoConfig::Notify(nsITimer* timer) {
+ downloadAutoConfig();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAutoConfig::GetName(nsACString& aName) {
+ aName.AssignLiteral("nsAutoConfig");
+ return NS_OK;
+}
+
+/* Observe() is called twice: once at the instantiation time and other
+ after the profile is set. It doesn't do anything but return NS_OK during the
+ creation time. Second time it calls downloadAutoConfig().
+*/
+
+NS_IMETHODIMP nsAutoConfig::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* someData) {
+ nsresult rv = NS_OK;
+ if (!nsCRT::strcmp(aTopic, "profile-after-change")) {
+ // We will be calling downloadAutoConfig even if there is no profile
+ // name. Nothing will be passed as a parameter to the URL and the
+ // default case will be picked up by the script.
+
+ rv = downloadAutoConfig();
+ }
+
+ return rv;
+}
+
+nsresult nsAutoConfig::downloadAutoConfig() {
+ nsresult rv;
+ nsAutoCString emailAddr;
+ static bool firstTime = true;
+
+ if (mConfigURL.IsEmpty()) {
+ MOZ_LOG(MCD, LogLevel::Debug,
+ ("global config url is empty - did you set "
+ "autoadmin.global_config_url?\n"));
+ NS_WARNING("AutoConfig called without global_config_url");
+ return NS_OK;
+ }
+
+ // If there is an email address appended as an argument to the ConfigURL
+ // in the previous read, we need to remove it when timer kicks in and
+ // downloads the autoconfig file again.
+ // If necessary, the email address will be added again as an argument.
+ int32_t index = mConfigURL.RFindChar((char16_t)'?');
+ if (index != -1) mConfigURL.Truncate(index);
+
+ // Clean up the previous read, the new read is going to use the same buffer
+ if (!mBuf.IsEmpty()) mBuf.Truncate(0);
+
+ // Get the preferences branch and save it to the member variable
+ if (!mPrefBranch) {
+ nsCOMPtr<nsIPrefService> prefs =
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = prefs->GetBranch(nullptr, getter_AddRefs(mPrefBranch));
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // Check to see if the network is online/offline
+ nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ bool offline;
+ rv = ios->GetOffline(&offline);
+ if (NS_FAILED(rv)) return rv;
+
+ if (offline) {
+ bool offlineFailover;
+ rv = mPrefBranch->GetBoolPref("autoadmin.offline_failover",
+ &offlineFailover);
+ // Read the failover.jsc if the network is offline and the pref says so
+ if (NS_SUCCEEDED(rv) && offlineFailover) return readOfflineFile();
+ }
+
+ /* Append user's identity at the end of the URL if the pref says so.
+ First we are checking for the user's email address but if it is not
+ available in the case where the client is used without messenger, user's
+ profile name will be used as an unique identifier
+ */
+ bool appendMail;
+ rv = mPrefBranch->GetBoolPref("autoadmin.append_emailaddr", &appendMail);
+ if (NS_SUCCEEDED(rv) && appendMail) {
+ rv = getEmailAddr(emailAddr);
+ if (NS_SUCCEEDED(rv) && emailAddr.get()) {
+ /* Adding the unique identifier at the end of autoconfig URL.
+ In this case the autoconfig URL is a script and
+ emailAddr as passed as an argument
+ */
+ mConfigURL.Append('?');
+ mConfigURL.Append(emailAddr);
+ }
+ }
+
+ // create a new url
+ nsCOMPtr<nsIURI> url;
+ nsCOMPtr<nsIChannel> channel;
+
+ rv = NS_NewURI(getter_AddRefs(url), mConfigURL);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(
+ MCD, LogLevel::Debug,
+ ("failed to create URL - is autoadmin.global_config_url valid? - %s\n",
+ mConfigURL.get()));
+ return rv;
+ }
+
+ MOZ_LOG(MCD, LogLevel::Debug, ("running MCD url %s\n", mConfigURL.get()));
+ // open a channel for the url
+ rv = NS_NewChannel(
+ getter_AddRefs(channel), url, nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // nsICookieJarSettings
+ nullptr, // PerformanceStorage
+ nullptr, // loadGroup
+ nullptr, // aCallbacks
+ nsIRequest::INHIBIT_PERSISTENT_CACHING | nsIRequest::LOAD_BYPASS_CACHE);
+
+ if (NS_FAILED(rv)) return rv;
+
+ rv = channel->AsyncOpen(this);
+ if (NS_FAILED(rv)) {
+ readOfflineFile();
+ return rv;
+ }
+
+ // Set a repeating timer if the pref is set.
+ // This is to be done only once.
+ // Also We are having the event queue processing only for the startup
+ // It is not needed with the repeating timer.
+ if (firstTime) {
+ firstTime = false;
+
+ /* process events until we're finished. AutoConfig.jsc reading needs
+ to be finished before the browser starts loading up
+ We are waiting for the mLoaded which will be set through
+ onStopRequest or readOfflineFile methods
+ There is a possibility of deadlock so we need to make sure
+ that mLoaded will be set to true in any case (success/failure)
+ */
+
+ if (!mozilla::SpinEventLoopUntil([&]() { return mLoaded; })) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t minutes;
+ rv = mPrefBranch->GetIntPref("autoadmin.refresh_interval", &minutes);
+ if (NS_SUCCEEDED(rv) && minutes > 0) {
+ // Create a new timer and pass this nsAutoConfig
+ // object as a timer callback.
+ MOZ_TRY_VAR(mTimer,
+ NS_NewTimerWithCallback(this, minutes * 60 * 1000,
+ nsITimer::TYPE_REPEATING_SLACK));
+ }
+ } // first_time
+
+ return NS_OK;
+} // nsPref::downloadAutoConfig()
+
+nsresult nsAutoConfig::readOfflineFile() {
+ nsresult rv;
+
+ /* Releasing the lock to allow main thread to start
+ execution. At this point we do not need to stall
+ the thread since all network activities are done.
+ */
+ mLoaded = true;
+
+ bool failCache;
+ rv = mPrefBranch->GetBoolPref("autoadmin.failover_to_cached", &failCache);
+ if (NS_SUCCEEDED(rv) && !failCache) {
+ // disable network connections and return.
+
+ nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ bool offline;
+ rv = ios->GetOffline(&offline);
+ if (NS_FAILED(rv)) return rv;
+
+ if (!offline) {
+ rv = ios->SetOffline(true);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // lock the "network.online" prference so user cannot toggle back to
+ // online mode.
+ rv = mPrefBranch->SetBoolPref("network.online", false);
+ if (NS_FAILED(rv)) return rv;
+
+ mPrefBranch->LockPref("network.online");
+ return NS_OK;
+ }
+
+ /* faiover_to_cached is set to true so
+ Open the file and read the content.
+ execute the javascript file
+ */
+
+ nsCOMPtr<nsIFile> failoverFile;
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(failoverFile));
+ if (NS_FAILED(rv)) return rv;
+
+ failoverFile->AppendNative("failover.jsc"_ns);
+ rv = evaluateLocalFile(failoverFile);
+ if (NS_FAILED(rv))
+ NS_WARNING("Couldn't open failover.jsc, going back to default prefs");
+ return NS_OK;
+}
+
+nsresult nsAutoConfig::evaluateLocalFile(nsIFile* file) {
+ nsresult rv;
+ nsCOMPtr<nsIInputStream> inStr;
+
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), file);
+ if (NS_FAILED(rv)) return rv;
+
+ int64_t fileSize;
+ file->GetFileSize(&fileSize);
+ uint32_t fs = fileSize; // Converting 64 bit structure to unsigned int
+ char* buf = (char*)malloc(fs * sizeof(char));
+ if (!buf) return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t amt = 0;
+ rv = inStr->Read(buf, fs, &amt);
+ if (NS_SUCCEEDED(rv)) {
+ EvaluateAdminConfigScript(buf, fs, nullptr, false, true, false);
+ }
+ inStr->Close();
+ free(buf);
+ return rv;
+}
+
+nsresult nsAutoConfig::writeFailoverFile() {
+ nsresult rv;
+ nsCOMPtr<nsIFile> failoverFile;
+ nsCOMPtr<nsIOutputStream> outStr;
+ uint32_t amt;
+
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(failoverFile));
+ if (NS_FAILED(rv)) return rv;
+
+ failoverFile->AppendNative("failover.jsc"_ns);
+
+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStr), failoverFile);
+ if (NS_FAILED(rv)) return rv;
+ rv = outStr->Write(mBuf.get(), mBuf.Length(), &amt);
+ outStr->Close();
+ return rv;
+}
+
+nsresult nsAutoConfig::getEmailAddr(nsACString& emailAddr) {
+ nsresult rv;
+ nsAutoCString prefValue;
+
+ /* Getting an email address through set of three preferences:
+ First getting a default account with
+ "mail.accountmanager.defaultaccount"
+ second getting an associated id with the default account
+ Third getting an email address with id
+ */
+
+ rv =
+ mPrefBranch->GetCharPref("mail.accountmanager.defaultaccount", prefValue);
+ if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty()) {
+ emailAddr = "mail.account."_ns + prefValue + ".identities"_ns;
+ rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(),
+ prefValue);
+ if (NS_FAILED(rv) || prefValue.IsEmpty())
+ return PromptForEMailAddress(emailAddr);
+ int32_t commandIndex = prefValue.FindChar(',');
+ if (commandIndex != kNotFound) prefValue.Truncate(commandIndex);
+ emailAddr = "mail.identity."_ns + prefValue + ".useremail"_ns;
+ rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(),
+ prefValue);
+ if (NS_FAILED(rv) || prefValue.IsEmpty())
+ return PromptForEMailAddress(emailAddr);
+ emailAddr = prefValue;
+ } else {
+ // look for 4.x pref in case we just migrated.
+ rv = mPrefBranch->GetCharPref("mail.identity.useremail", prefValue);
+ if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty())
+ emailAddr = prefValue;
+ else
+ PromptForEMailAddress(emailAddr);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsAutoConfig::PromptForEMailAddress(nsACString& emailAddress) {
+ nsresult rv;
+ nsCOMPtr<nsIPromptService> promptService =
+ do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle(
+ "chrome://autoconfig/locale/autoconfig.properties",
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString title;
+ rv = bundle->GetStringFromName("emailPromptTitle", title);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString err;
+ rv = bundle->GetStringFromName("emailPromptMsg", err);
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool check = false;
+ nsString emailResult;
+ bool success;
+ rv = promptService->Prompt(nullptr, title.get(), err.get(),
+ getter_Copies(emailResult), nullptr, &check,
+ &success);
+ if (!success) return NS_ERROR_FAILURE;
+ NS_ENSURE_SUCCESS(rv, rv);
+ LossyCopyUTF16toASCII(emailResult, emailAddress);
+ return NS_OK;
+}