tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsAutoConfig.cpp (14788B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "nsAutoConfig.h"
      7 #include "nsJSConfigTriggers.h"
      8 
      9 #include "nsIURI.h"
     10 #include "nsIHttpChannel.h"
     11 #include "nsThreadUtils.h"
     12 #include "nsAppDirectoryServiceDefs.h"
     13 #include "nsIObserverService.h"
     14 #include "nsLiteralString.h"
     15 #include "nsIPromptService.h"
     16 #include "nsIInputStream.h"
     17 #include "nsIOutputStream.h"
     18 #include "nsIPrefBranch.h"
     19 #include "nsIPrefService.h"
     20 #include "nsIStringBundle.h"
     21 #include "nsContentUtils.h"
     22 #include "nsCRT.h"
     23 #include "nsNetCID.h"
     24 #include "nsNetUtil.h"
     25 #include "nspr.h"
     26 #include <algorithm>
     27 
     28 #include "mozilla/IntegerPrintfMacros.h"
     29 #include "mozilla/Logging.h"
     30 #include "mozilla/SpinEventLoopUntil.h"
     31 #include "mozilla/Try.h"
     32 
     33 using mozilla::LogLevel;
     34 
     35 mozilla::LazyLogModule MCD("MCD");
     36 
     37 // nsISupports Implementation
     38 
     39 NS_IMPL_ISUPPORTS(nsAutoConfig, nsITimerCallback, nsIStreamListener,
     40                  nsIObserver, nsIRequestObserver, nsISupportsWeakReference,
     41                  nsINamed)
     42 
     43 nsAutoConfig::nsAutoConfig() {}
     44 
     45 nsresult nsAutoConfig::Init() {
     46  // member initializers and constructor code
     47 
     48  nsresult rv;
     49  mLoaded = false;
     50 
     51  // Registering the object as an observer to the profile-after-change topic
     52  nsCOMPtr<nsIObserverService> observerService =
     53      do_GetService("@mozilla.org/observer-service;1", &rv);
     54  if (NS_FAILED(rv)) return rv;
     55 
     56  rv = observerService->AddObserver(this, "profile-after-change", true);
     57 
     58  return rv;
     59 }
     60 
     61 nsAutoConfig::~nsAutoConfig() {}
     62 
     63 void nsAutoConfig::SetConfigURL(const char* aConfigURL) {
     64  mConfigURL.Assign(aConfigURL);
     65 }
     66 
     67 NS_IMETHODIMP
     68 nsAutoConfig::OnStartRequest(nsIRequest* request) { return NS_OK; }
     69 
     70 NS_IMETHODIMP
     71 nsAutoConfig::OnDataAvailable(nsIRequest* request, nsIInputStream* aIStream,
     72                              uint64_t aSourceOffset, uint32_t aLength) {
     73  uint32_t amt, size;
     74  nsresult rv;
     75  char buf[1024];
     76 
     77  while (aLength) {
     78    size = std::min<size_t>(aLength, sizeof(buf));
     79    rv = aIStream->Read(buf, size, &amt);
     80    if (NS_FAILED(rv)) return rv;
     81    mBuf.Append(buf, amt);
     82    aLength -= amt;
     83  }
     84  return NS_OK;
     85 }
     86 
     87 NS_IMETHODIMP
     88 nsAutoConfig::OnStopRequest(nsIRequest* request, nsresult aStatus) {
     89  nsresult rv;
     90 
     91  // If the request is failed, go read the failover.jsc file
     92  if (NS_FAILED(aStatus)) {
     93    MOZ_LOG(MCD, LogLevel::Debug,
     94            ("mcd request failed with status %" PRIx32 "\n",
     95             static_cast<uint32_t>(aStatus)));
     96    return readOfflineFile();
     97  }
     98 
     99  // Checking for the http response, if failure go read the failover file.
    100  nsCOMPtr<nsIHttpChannel> pHTTPCon(do_QueryInterface(request));
    101  if (pHTTPCon) {
    102    uint32_t httpStatus;
    103    rv = pHTTPCon->GetResponseStatus(&httpStatus);
    104    if (NS_FAILED(rv) || httpStatus != 200) {
    105      MOZ_LOG(MCD, LogLevel::Debug,
    106              ("mcd http request failed with status %x\n", httpStatus));
    107      return readOfflineFile();
    108    }
    109  }
    110 
    111  // Send the autoconfig.jsc to javascript engine.
    112 
    113  rv = EvaluateAdminConfigScript(mBuf.get(), mBuf.Length(), nullptr, false,
    114                                 true, false);
    115  if (NS_SUCCEEDED(rv)) {
    116    // Write the autoconfig.jsc to failover.jsc (cached copy)
    117    rv = writeFailoverFile();
    118 
    119    if (NS_FAILED(rv)) NS_WARNING("Error writing failover.jsc file");
    120 
    121    // Releasing the lock to allow the main thread to start execution
    122    mLoaded = true;
    123 
    124    return NS_OK;
    125  }
    126  // there is an error in parsing of the autoconfig file.
    127  NS_WARNING(
    128      "Error reading autoconfig.jsc from the network, reading the offline "
    129      "version");
    130  return readOfflineFile();
    131 }
    132 
    133 // Notify method as a TimerCallBack function
    134 NS_IMETHODIMP nsAutoConfig::Notify(nsITimer* timer) {
    135  downloadAutoConfig();
    136  return NS_OK;
    137 }
    138 
    139 NS_IMETHODIMP
    140 nsAutoConfig::GetName(nsACString& aName) {
    141  aName.AssignLiteral("nsAutoConfig");
    142  return NS_OK;
    143 }
    144 
    145 /* Observe() is called twice: once at the instantiation time and other
    146   after the profile is set. It doesn't do anything but return NS_OK during the
    147   creation time. Second time it calls  downloadAutoConfig().
    148 */
    149 
    150 NS_IMETHODIMP nsAutoConfig::Observe(nsISupports* aSubject, const char* aTopic,
    151                                    const char16_t* someData) {
    152  nsresult rv = NS_OK;
    153  if (!nsCRT::strcmp(aTopic, "profile-after-change")) {
    154    // We will be calling downloadAutoConfig even if there is no profile
    155    // name. Nothing will be passed as a parameter to the URL and the
    156    // default case will be picked up by the script.
    157 
    158    rv = downloadAutoConfig();
    159  }
    160 
    161  return rv;
    162 }
    163 
    164 nsresult nsAutoConfig::downloadAutoConfig() {
    165  nsresult rv;
    166  nsAutoCString emailAddr;
    167  static bool firstTime = true;
    168 
    169  if (mConfigURL.IsEmpty()) {
    170    MOZ_LOG(MCD, LogLevel::Debug,
    171            ("global config url is empty - did you set "
    172             "autoadmin.global_config_url?\n"));
    173    NS_WARNING("AutoConfig called without global_config_url");
    174    return NS_OK;
    175  }
    176 
    177  // If there is an email address appended as an argument to the ConfigURL
    178  // in the previous read, we need to remove it when timer kicks in and
    179  // downloads the autoconfig file again.
    180  // If necessary, the email address will be added again as an argument.
    181  int32_t index = mConfigURL.RFindChar((char16_t)'?');
    182  if (index != -1) mConfigURL.Truncate(index);
    183 
    184  // Clean up the previous read, the new read is going to use the same buffer
    185  if (!mBuf.IsEmpty()) mBuf.Truncate(0);
    186 
    187  // Get the preferences branch and save it to the member variable
    188  if (!mPrefBranch) {
    189    nsCOMPtr<nsIPrefService> prefs =
    190        do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
    191    if (NS_FAILED(rv)) return rv;
    192 
    193    rv = prefs->GetBranch(nullptr, getter_AddRefs(mPrefBranch));
    194    if (NS_FAILED(rv)) return rv;
    195  }
    196 
    197  // Check to see if the network is online/offline
    198  nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
    199  if (NS_FAILED(rv)) return rv;
    200 
    201  bool offline;
    202  rv = ios->GetOffline(&offline);
    203  if (NS_FAILED(rv)) return rv;
    204 
    205  if (offline) {
    206    bool offlineFailover;
    207    rv = mPrefBranch->GetBoolPref("autoadmin.offline_failover",
    208                                  &offlineFailover);
    209    // Read the failover.jsc if the network is offline and the pref says so
    210    if (NS_SUCCEEDED(rv) && offlineFailover) return readOfflineFile();
    211  }
    212 
    213  /* Append user's identity at the end of the URL if the pref says so.
    214     First we are checking for the user's email address but if it is not
    215     available in the case where the client is used without messenger, user's
    216     profile name will be used as an unique identifier
    217  */
    218  bool appendMail;
    219  rv = mPrefBranch->GetBoolPref("autoadmin.append_emailaddr", &appendMail);
    220  if (NS_SUCCEEDED(rv) && appendMail) {
    221    rv = getEmailAddr(emailAddr);
    222    if (NS_SUCCEEDED(rv) && emailAddr.get()) {
    223      /* Adding the unique identifier at the end of autoconfig URL.
    224         In this case the autoconfig URL is a script and
    225         emailAddr as passed as an argument
    226      */
    227      mConfigURL.Append('?');
    228      mConfigURL.Append(emailAddr);
    229    }
    230  }
    231 
    232  // create a new url
    233  nsCOMPtr<nsIURI> url;
    234  nsCOMPtr<nsIChannel> channel;
    235 
    236  rv = NS_NewURI(getter_AddRefs(url), mConfigURL);
    237  if (NS_FAILED(rv)) {
    238    MOZ_LOG(
    239        MCD, LogLevel::Debug,
    240        ("failed to create URL - is autoadmin.global_config_url valid? - %s\n",
    241         mConfigURL.get()));
    242    return rv;
    243  }
    244 
    245  MOZ_LOG(MCD, LogLevel::Debug, ("running MCD url %s\n", mConfigURL.get()));
    246  // open a channel for the url
    247  rv = NS_NewChannel(
    248      getter_AddRefs(channel), url, nsContentUtils::GetSystemPrincipal(),
    249      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
    250      nsIContentPolicy::TYPE_OTHER,
    251      nullptr,  // nsICookieJarSettings
    252      nullptr,  // PerformanceStorage
    253      nullptr,  // loadGroup
    254      nullptr,  // aCallbacks
    255      nsIRequest::INHIBIT_PERSISTENT_CACHING | nsIRequest::LOAD_BYPASS_CACHE);
    256 
    257  if (NS_FAILED(rv)) return rv;
    258 
    259  rv = channel->AsyncOpen(this);
    260  if (NS_FAILED(rv)) {
    261    readOfflineFile();
    262    return rv;
    263  }
    264 
    265  // Set a repeating timer if the pref is set.
    266  // This is to be done only once.
    267  // Also We are having the event queue processing only for the startup
    268  // It is not needed with the repeating timer.
    269  if (firstTime) {
    270    firstTime = false;
    271 
    272    /* process events until we're finished. AutoConfig.jsc reading needs
    273       to be finished before the browser starts loading up
    274       We are waiting for the mLoaded which will be set through
    275       onStopRequest or readOfflineFile methods
    276       There is a possibility of deadlock so we need to make sure
    277       that mLoaded will be set to true in any case (success/failure)
    278    */
    279 
    280    if (!mozilla::SpinEventLoopUntil("nsAutoConfig::downloadAutoConfig"_ns,
    281                                     [&]() { return mLoaded; })) {
    282      return NS_ERROR_FAILURE;
    283    }
    284 
    285    int32_t minutes;
    286    rv = mPrefBranch->GetIntPref("autoadmin.refresh_interval", &minutes);
    287    if (NS_SUCCEEDED(rv) && minutes > 0) {
    288      // Create a new timer and pass this nsAutoConfig
    289      // object as a timer callback.
    290      mTimer = MOZ_TRY(NS_NewTimerWithCallback(this, minutes * 60 * 1000,
    291                                               nsITimer::TYPE_REPEATING_SLACK));
    292    }
    293  }  // first_time
    294 
    295  return NS_OK;
    296 }  // nsPref::downloadAutoConfig()
    297 
    298 nsresult nsAutoConfig::readOfflineFile() {
    299  nsresult rv;
    300 
    301  /* Releasing the lock to allow main thread to start
    302     execution. At this point we do not need to stall
    303     the thread since all network activities are done.
    304  */
    305  mLoaded = true;
    306 
    307  bool failCache;
    308  rv = mPrefBranch->GetBoolPref("autoadmin.failover_to_cached", &failCache);
    309  if (NS_SUCCEEDED(rv) && !failCache) {
    310    // disable network connections and return.
    311 
    312    nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
    313    if (NS_FAILED(rv)) return rv;
    314 
    315    bool offline;
    316    rv = ios->GetOffline(&offline);
    317    if (NS_FAILED(rv)) return rv;
    318 
    319    if (!offline) {
    320      rv = ios->SetOffline(true);
    321      if (NS_FAILED(rv)) return rv;
    322    }
    323 
    324    // lock the "network.online" prference so user cannot toggle back to
    325    // online mode.
    326    rv = mPrefBranch->SetBoolPref("network.online", false);
    327    if (NS_FAILED(rv)) return rv;
    328 
    329    mPrefBranch->LockPref("network.online");
    330    return NS_OK;
    331  }
    332 
    333  /* faiover_to_cached is set to true so
    334     Open the file and read the content.
    335     execute the javascript file
    336  */
    337 
    338  nsCOMPtr<nsIFile> failoverFile;
    339  rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
    340                              getter_AddRefs(failoverFile));
    341  if (NS_FAILED(rv)) return rv;
    342 
    343  failoverFile->AppendNative("failover.jsc"_ns);
    344  rv = evaluateLocalFile(failoverFile);
    345  if (NS_FAILED(rv))
    346    NS_WARNING("Couldn't open failover.jsc, going back to default prefs");
    347  return NS_OK;
    348 }
    349 
    350 nsresult nsAutoConfig::evaluateLocalFile(nsIFile* file) {
    351  nsresult rv;
    352  nsCOMPtr<nsIInputStream> inStr;
    353 
    354  rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), file);
    355  if (NS_FAILED(rv)) return rv;
    356 
    357  int64_t fileSize;
    358  file->GetFileSize(&fileSize);
    359  uint32_t fs = fileSize;  // Converting 64 bit structure to unsigned int
    360  char* buf = (char*)malloc(fs * sizeof(char));
    361  if (!buf) return NS_ERROR_OUT_OF_MEMORY;
    362 
    363  uint32_t amt = 0;
    364  rv = inStr->Read(buf, fs, &amt);
    365  if (NS_SUCCEEDED(rv)) {
    366    EvaluateAdminConfigScript(buf, fs, nullptr, false, true, false);
    367  }
    368  inStr->Close();
    369  free(buf);
    370  return rv;
    371 }
    372 
    373 nsresult nsAutoConfig::writeFailoverFile() {
    374  nsresult rv;
    375  nsCOMPtr<nsIFile> failoverFile;
    376  nsCOMPtr<nsIOutputStream> outStr;
    377  uint32_t amt;
    378 
    379  rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
    380                              getter_AddRefs(failoverFile));
    381  if (NS_FAILED(rv)) return rv;
    382 
    383  failoverFile->AppendNative("failover.jsc"_ns);
    384 
    385  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStr), failoverFile);
    386  if (NS_FAILED(rv)) return rv;
    387  rv = outStr->Write(mBuf.get(), mBuf.Length(), &amt);
    388  outStr->Close();
    389  return rv;
    390 }
    391 
    392 nsresult nsAutoConfig::getEmailAddr(nsACString& emailAddr) {
    393  nsresult rv;
    394  nsAutoCString prefValue;
    395 
    396  /* Getting an email address through set of three preferences:
    397     First getting a default account with
    398     "mail.accountmanager.defaultaccount"
    399     second getting an associated id with the default account
    400     Third getting an email address with id
    401  */
    402 
    403  rv =
    404      mPrefBranch->GetCharPref("mail.accountmanager.defaultaccount", prefValue);
    405  if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty()) {
    406    emailAddr = "mail.account."_ns + prefValue + ".identities"_ns;
    407    rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(),
    408                                  prefValue);
    409    if (NS_FAILED(rv) || prefValue.IsEmpty())
    410      return PromptForEMailAddress(emailAddr);
    411    int32_t commandIndex = prefValue.FindChar(',');
    412    if (commandIndex != kNotFound) prefValue.Truncate(commandIndex);
    413    emailAddr = "mail.identity."_ns + prefValue + ".useremail"_ns;
    414    rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(),
    415                                  prefValue);
    416    if (NS_FAILED(rv) || prefValue.IsEmpty())
    417      return PromptForEMailAddress(emailAddr);
    418    emailAddr = prefValue;
    419  } else {
    420    // look for 4.x pref in case we just migrated.
    421    rv = mPrefBranch->GetCharPref("mail.identity.useremail", prefValue);
    422    if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty())
    423      emailAddr = prefValue;
    424    else
    425      PromptForEMailAddress(emailAddr);
    426  }
    427 
    428  return NS_OK;
    429 }
    430 
    431 nsresult nsAutoConfig::PromptForEMailAddress(nsACString& emailAddress) {
    432  nsresult rv;
    433  nsCOMPtr<nsIPromptService> promptService =
    434      do_GetService("@mozilla.org/prompter;1", &rv);
    435  NS_ENSURE_SUCCESS(rv, rv);
    436  nsCOMPtr<nsIStringBundleService> bundleService =
    437      do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
    438  NS_ENSURE_SUCCESS(rv, rv);
    439 
    440  nsCOMPtr<nsIStringBundle> bundle;
    441  rv = bundleService->CreateBundle(
    442      "chrome://autoconfig/locale/autoconfig.properties",
    443      getter_AddRefs(bundle));
    444  NS_ENSURE_SUCCESS(rv, rv);
    445 
    446  nsAutoString title;
    447  rv = bundle->GetStringFromName("emailPromptTitle", title);
    448  NS_ENSURE_SUCCESS(rv, rv);
    449 
    450  nsAutoString err;
    451  rv = bundle->GetStringFromName("emailPromptMsg", err);
    452  NS_ENSURE_SUCCESS(rv, rv);
    453  bool check = false;
    454  nsString emailResult;
    455  bool success;
    456  rv = promptService->Prompt(nullptr, title.get(), err.get(),
    457                             getter_Copies(emailResult), nullptr, &check,
    458                             &success);
    459  if (!success) return NS_ERROR_FAILURE;
    460  NS_ENSURE_SUCCESS(rv, rv);
    461  LossyCopyUTF16toASCII(emailResult, emailAddress);
    462  return NS_OK;
    463 }