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.cpp532
1 files changed, 532 insertions, 0 deletions
diff --git a/extensions/pref/autoconfig/src/nsAutoConfig.cpp b/extensions/pref/autoconfig/src/nsAutoConfig.cpp
new file mode 100644
index 000000000..08f406093
--- /dev/null
+++ b/extensions/pref/autoconfig/src/nsAutoConfig.cpp
@@ -0,0 +1,532 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsAutoConfig.h"
+#include "nsIURI.h"
+#include "nsIHttpChannel.h"
+#include "nsIFileStreams.h"
+#include "nsThreadUtils.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "prmem.h"
+#include "nsIObserverService.h"
+#include "nsLiteralString.h"
+#include "nsIPromptService.h"
+#include "nsIServiceManager.h"
+#include "nsIStringBundle.h"
+#include "nsContentUtils.h"
+#include "nsCRT.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nspr.h"
+#include <algorithm>
+
+#include "mozilla/Logging.h"
+
+using mozilla::LogLevel;
+
+mozilla::LazyLogModule MCD("MCD");
+
+extern nsresult EvaluateAdminConfigScript(const char *js_buffer, size_t length,
+ const char *filename,
+ bool bGlobalContext,
+ bool bCallbacks,
+ bool skipFirstLine);
+
+// nsISupports Implementation
+
+NS_IMPL_ISUPPORTS(nsAutoConfig, nsIAutoConfig, nsITimerCallback, nsIStreamListener, nsIObserver, nsIRequestObserver, nsISupportsWeakReference)
+
+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()
+{
+}
+
+// attribute string configURL
+NS_IMETHODIMP nsAutoConfig::GetConfigURL(char **aConfigURL)
+{
+ if (!aConfigURL)
+ return NS_ERROR_NULL_POINTER;
+
+ if (mConfigURL.IsEmpty()) {
+ *aConfigURL = nullptr;
+ return NS_OK;
+ }
+
+ *aConfigURL = ToNewCString(mConfigURL);
+ if (!*aConfigURL)
+ return NS_ERROR_OUT_OF_MEMORY;
+ return NS_OK;
+}
+NS_IMETHODIMP nsAutoConfig::SetConfigURL(const char *aConfigURL)
+{
+ if (!aConfigURL)
+ return NS_ERROR_NULL_POINTER;
+ mConfigURL.Assign(aConfigURL);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAutoConfig::OnStartRequest(nsIRequest *request, nsISupports *context)
+{
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsAutoConfig::OnDataAvailable(nsIRequest *request,
+ nsISupports *context,
+ 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, nsISupports *context,
+ 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 %x\n", 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;
+ pHTTPCon->GetResponseStatus(&httpStatus);
+ if (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;
+}
+
+/* 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;
+ nsXPIDLCString urlName;
+ 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.get(), nullptr, nullptr);
+ 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_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // loadGroup
+ nullptr, // aCallbacks
+ nsIRequest::INHIBIT_PERSISTENT_CACHING |
+ nsIRequest::LOAD_BYPASS_CACHE);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = channel->AsyncOpen2(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;
+
+ // Getting the current thread. If we start an AsyncOpen, the thread
+ // needs to wait before the reading of autoconfig is done
+
+ nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+ NS_ENSURE_STATE(thread);
+
+ /* 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)
+ */
+
+ while (!mLoaded)
+ NS_ENSURE_STATE(NS_ProcessNextEvent(thread));
+
+ 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.
+ mTimer = do_CreateInstance("@mozilla.org/timer;1",&rv);
+ if (NS_FAILED(rv))
+ return rv;
+ rv = mTimer->InitWithCallback(this, minutes * 60 * 1000,
+ nsITimer::TYPE_REPEATING_SLACK);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ } //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(NS_LITERAL_CSTRING("failover.jsc"));
+ 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 *)PR_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();
+ PR_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(NS_LITERAL_CSTRING("failover.jsc"));
+
+ 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;
+ nsXPIDLCString 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",
+ getter_Copies(prefValue));
+ if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty()) {
+ emailAddr = NS_LITERAL_CSTRING("mail.account.") +
+ prefValue + NS_LITERAL_CSTRING(".identities");
+ rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(),
+ getter_Copies(prefValue));
+ if (NS_FAILED(rv) || prefValue.IsEmpty())
+ return PromptForEMailAddress(emailAddr);
+ int32_t commandIndex = prefValue.FindChar(',');
+ if (commandIndex != kNotFound)
+ prefValue.Truncate(commandIndex);
+ emailAddr = NS_LITERAL_CSTRING("mail.identity.") +
+ prefValue + NS_LITERAL_CSTRING(".useremail");
+ rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(),
+ getter_Copies(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",
+ getter_Copies(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);
+
+ nsXPIDLString title;
+ rv = bundle->GetStringFromName(u"emailPromptTitle", getter_Copies(title));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsXPIDLString err;
+ rv = bundle->GetStringFromName(u"emailPromptMsg", getter_Copies(err));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool check = false;
+ nsXPIDLString 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;
+}