diff options
Diffstat (limited to 'toolkit/xre/nsXREDirProvider.cpp')
-rw-r--r-- | toolkit/xre/nsXREDirProvider.cpp | 1769 |
1 files changed, 1769 insertions, 0 deletions
diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp new file mode 100644 index 0000000000..d9bcdb1f4e --- /dev/null +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -0,0 +1,1769 @@ +/* -*- Mode: C++; 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/. */ + +#include "nsAppRunner.h" +#include "nsXREDirProvider.h" +#ifndef ANDROID +# include "commonupdatedir.h" +#endif + +#include "jsapi.h" +#include "xpcpublic.h" + +#include "nsIAppStartup.h" +#include "nsIFile.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsISimpleEnumerator.h" +#include "nsIToolkitProfileService.h" +#include "nsIXULRuntime.h" +#include "commonupdatedir.h" + +#include "nsAppDirectoryServiceDefs.h" +#include "nsDirectoryServiceDefs.h" +#include "nsDirectoryServiceUtils.h" +#include "nsXULAppAPI.h" +#include "nsCategoryManagerUtils.h" + +#include "nsDependentString.h" +#include "nsCOMArray.h" +#include "nsArrayEnumerator.h" +#include "nsEnumeratorUtils.h" +#include "nsReadableUtils.h" + +#include "GeckoProfiler.h" +#include "SpecialSystemDirectory.h" + +#include "mozilla/dom/ScriptSettings.h" + +#include "mozilla/AutoRestore.h" +#include "mozilla/Components.h" +#include "mozilla/Services.h" +#include "mozilla/Omnijar.h" +#include "mozilla/Preferences.h" +#include "mozilla/Telemetry.h" +#include "mozilla/XREAppData.h" +#include "nsPrintfCString.h" + +#include <stdlib.h> + +#ifdef XP_WIN +# include <windows.h> +# include <shlobj.h> +# include "WinUtils.h" +#endif +#ifdef XP_MACOSX +# include "nsILocalFileMac.h" +// for chflags() +# include <sys/stat.h> +# include <unistd.h> +#endif +#ifdef XP_UNIX +# include <ctype.h> +#endif +#ifdef XP_IOS +# include "UIKitDirProvider.h" +#endif + +#if defined(MOZ_SANDBOX) +# include "mozilla/SandboxSettings.h" +# include "nsIUUIDGenerator.h" +# include "mozilla/Unused.h" +# if defined(XP_WIN) +# include "sandboxBroker.h" +# endif +#endif + +#if defined(XP_MACOSX) +# define APP_REGISTRY_NAME "Application Registry" +#elif defined(XP_WIN) +# define APP_REGISTRY_NAME "registry.dat" +#else +# define APP_REGISTRY_NAME "appreg" +#endif + +#define PREF_OVERRIDE_DIRNAME "preferences" + +#if defined(MOZ_SANDBOX) +static already_AddRefed<nsIFile> GetProcessSandboxTempDir( + GeckoProcessType type); +static nsresult DeleteDirIfExists(nsIFile* dir); +static bool IsContentSandboxDisabled(); +static const char* GetProcessTempBaseDirKey(); +static already_AddRefed<nsIFile> CreateProcessSandboxTempDir( + GeckoProcessType procType); +#endif + +nsXREDirProvider* gDirServiceProvider = nullptr; +nsIFile* gDataDirHomeLocal = nullptr; +nsIFile* gDataDirHome = nullptr; +nsCOMPtr<nsIFile> gDataDirProfileLocal = nullptr; +nsCOMPtr<nsIFile> gDataDirProfile = nullptr; + +// These are required to allow nsXREDirProvider to be usable in xpcshell tests. +// where gAppData is null. +#if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_UNIX) +static const char* GetAppName() { + if (gAppData) { + return gAppData->name; + } + return nullptr; +} +#endif + +static const char* GetAppVendor() { + if (gAppData) { + return gAppData->vendor; + } + return nullptr; +} + +nsXREDirProvider::nsXREDirProvider() : mProfileNotified(false) { + gDirServiceProvider = this; +} + +nsXREDirProvider::~nsXREDirProvider() { + gDirServiceProvider = nullptr; + gDataDirHomeLocal = nullptr; + gDataDirHome = nullptr; +} + +already_AddRefed<nsXREDirProvider> nsXREDirProvider::GetSingleton() { + if (!gDirServiceProvider) { + new nsXREDirProvider(); // This sets gDirServiceProvider + } + return do_AddRef(gDirServiceProvider); +} + +nsresult nsXREDirProvider::Initialize( + nsIFile* aXULAppDir, nsIFile* aGREDir, + nsIDirectoryServiceProvider* aAppProvider) { + NS_ENSURE_ARG(aXULAppDir); + NS_ENSURE_ARG(aGREDir); + + mAppProvider = aAppProvider; + mXULAppDir = aXULAppDir; + mGREDir = aGREDir; +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + // The GRE directory can be used in sandbox rules, so we need to make sure + // it doesn't contain any junction points or symlinks or the sandbox will + // reject those rules. + if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks(mGREDir)) { + NS_WARNING("Failed to resolve GRE Dir."); + } + // If the mXULAppDir is different it lives below the mGREDir. To avoid + // confusion resolve that as well even though we don't need it for sandbox + // rules. Some tests rely on this for example. + if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks( + mXULAppDir)) { + NS_WARNING("Failed to resolve XUL App Dir."); + } +#endif + mGREDir->Clone(getter_AddRefs(mGREBinDir)); +#ifdef XP_MACOSX + mGREBinDir->SetNativeLeafName("MacOS"_ns); +#endif + + if (!mProfileDir) { + nsCOMPtr<nsIDirectoryServiceProvider> app(mAppProvider); + if (app) { + bool per = false; + app->GetFile(NS_APP_USER_PROFILE_50_DIR, &per, + getter_AddRefs(mProfileDir)); + NS_ASSERTION(per, "NS_APP_USER_PROFILE_50_DIR must be persistent!"); + NS_ASSERTION( + mProfileDir, + "NS_APP_USER_PROFILE_50_DIR not defined! This shouldn't happen!"); + } + } + + return NS_OK; +} + +nsresult nsXREDirProvider::SetProfile(nsIFile* aDir, nsIFile* aLocalDir) { + NS_ASSERTION(aDir && aLocalDir, "We don't support no-profile apps yet!"); + + nsresult rv; + + rv = EnsureDirectoryExists(aDir); + if (NS_FAILED(rv)) return rv; + + rv = EnsureDirectoryExists(aLocalDir); + if (NS_FAILED(rv)) return rv; + +#ifdef XP_MACOSX + bool same; + if (NS_SUCCEEDED(aDir->Equals(aLocalDir, &same)) && !same) { + // Ensure that the cache directory is not indexed by Spotlight + // (bug 718910). At least on OS X, the cache directory (under + // ~/Library/Caches/) is always the "local" user profile + // directory. This is confusing, since *both* user profile + // directories are "local" (they both exist under the user's + // home directory). But this usage dates back at least as far + // as the patch for bug 291033, where "local" seems to mean + // "suitable for temporary storage". Don't hide the cache + // directory if by some chance it and the "non-local" profile + // directory are the same -- there are bad side effects from + // hiding a profile directory under /Library/Application Support/ + // (see bug 801883). + nsAutoCString cacheDir; + if (NS_SUCCEEDED(aLocalDir->GetNativePath(cacheDir))) { + if (chflags(cacheDir.get(), UF_HIDDEN)) { + NS_WARNING("Failed to set Cache directory to HIDDEN."); + } + } + } +#endif + + mProfileDir = aDir; + mProfileLocalDir = aLocalDir; +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + // The profile directory can be used in sandbox rules, so we need to make sure + // it doesn't contain any junction points or symlinks or the sandbox will + // reject those rules. + if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks( + mProfileDir)) { + NS_WARNING("Failed to resolve Profile Dir."); + } +#endif + return NS_OK; +} + +NS_IMPL_QUERY_INTERFACE(nsXREDirProvider, nsIDirectoryServiceProvider, + nsIDirectoryServiceProvider2, nsIXREDirProvider, + nsIProfileStartup) + +NS_IMETHODIMP_(MozExternalRefCountType) +nsXREDirProvider::AddRef() { return 1; } + +NS_IMETHODIMP_(MozExternalRefCountType) +nsXREDirProvider::Release() { return 0; } + +nsresult nsXREDirProvider::GetUserProfilesRootDir(nsIFile** aResult) { + nsCOMPtr<nsIFile> file; + nsresult rv = GetUserDataDirectory(getter_AddRefs(file), false); + + if (NS_SUCCEEDED(rv)) { +#if !defined(XP_UNIX) || defined(XP_MACOSX) + rv = file->AppendNative("Profiles"_ns); +#endif + // We must create the profile directory here if it does not exist. + nsresult tmp = EnsureDirectoryExists(file); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + file.swap(*aResult); + return rv; +} + +nsresult nsXREDirProvider::GetUserProfilesLocalDir(nsIFile** aResult) { + nsCOMPtr<nsIFile> file; + nsresult rv = GetUserDataDirectory(getter_AddRefs(file), true); + + if (NS_SUCCEEDED(rv)) { +#if !defined(XP_UNIX) || defined(XP_MACOSX) + rv = file->AppendNative("Profiles"_ns); +#endif + // We must create the profile directory here if it does not exist. + nsresult tmp = EnsureDirectoryExists(file); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + file.swap(*aResult); + return NS_OK; +} + +#if defined(XP_UNIX) || defined(XP_MACOSX) +/** + * Get the directory that is the parent of the system-wide directories + * for extensions and native manifests. + * + * On OSX this is /Library/Application Support/Mozilla + * On Linux this is /usr/{lib,lib64}/mozilla + * (for 32- and 64-bit systems respsectively) + */ +static nsresult GetSystemParentDirectory(nsIFile** aFile) { + nsresult rv; + nsCOMPtr<nsIFile> localDir; +# if defined(XP_MACOSX) + rv = GetOSXFolderType(kOnSystemDisk, kApplicationSupportFolderType, + getter_AddRefs(localDir)); + if (NS_SUCCEEDED(rv)) { + rv = localDir->AppendNative("Mozilla"_ns); + } +# else + constexpr auto dirname = +# ifdef HAVE_USR_LIB64_DIR + "/usr/lib64/mozilla"_ns +# elif defined(__OpenBSD__) || defined(__FreeBSD__) + "/usr/local/lib/mozilla"_ns +# else + "/usr/lib/mozilla"_ns +# endif + ; + rv = NS_NewNativeLocalFile(dirname, false, getter_AddRefs(localDir)); +# endif + + if (NS_SUCCEEDED(rv)) { + localDir.forget(aFile); + } + return rv; +} +#endif + +NS_IMETHODIMP +nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent, + nsIFile** aFile) { + nsresult rv; + + bool gettingProfile = false; + + if (!strcmp(aProperty, NS_APP_USER_PROFILE_LOCAL_50_DIR)) { + // If XRE_NotifyProfile hasn't been called, don't fall through to + // mAppProvider on the profile keys. + if (!mProfileNotified) return NS_ERROR_FAILURE; + + if (mProfileLocalDir) return mProfileLocalDir->Clone(aFile); + + if (mAppProvider) + return mAppProvider->GetFile(aProperty, aPersistent, aFile); + + // This falls through to the case below + gettingProfile = true; + } + if (!strcmp(aProperty, NS_APP_USER_PROFILE_50_DIR) || gettingProfile) { + if (!mProfileNotified) return NS_ERROR_FAILURE; + + if (mProfileDir) return mProfileDir->Clone(aFile); + + if (mAppProvider) + return mAppProvider->GetFile(aProperty, aPersistent, aFile); + + // If we don't succeed here, bail early so that we aren't reentrant + // through the "GetProfileDir" call below. + return NS_ERROR_FAILURE; + } + + if (mAppProvider) { + rv = mAppProvider->GetFile(aProperty, aPersistent, aFile); + if (NS_SUCCEEDED(rv) && *aFile) return rv; + } + + *aPersistent = true; + + if (!strcmp(aProperty, NS_GRE_DIR)) { +#if defined(MOZ_WIDGET_ANDROID) + // On Android, libraries and other internal files are inside the APK, a zip + // file, so this folder doesn't really make sense. + return NS_ERROR_FAILURE; +#else + return mGREDir->Clone(aFile); +#endif + } else if (!strcmp(aProperty, NS_GRE_BIN_DIR)) { +#if defined(MOZ_WIDGET_ANDROID) + // Same as NS_GRE_DIR + return NS_ERROR_FAILURE; +#else + return mGREBinDir->Clone(aFile); +#endif + } else if (!strcmp(aProperty, NS_OS_CURRENT_PROCESS_DIR) || + !strcmp(aProperty, NS_APP_INSTALL_CLEANUP_DIR)) { + return GetAppDir()->Clone(aFile); + } + + rv = NS_ERROR_FAILURE; + nsCOMPtr<nsIFile> file; + + if (!strcmp(aProperty, NS_APP_PREF_DEFAULTS_50_DIR)) { +#if defined(MOZ_WIDGET_ANDROID) + // Same as NS_GRE_DIR + return NS_ERROR_FAILURE; +#else + // return the GRE default prefs directory here, and the app default prefs + // directory (if applicable) in NS_APP_PREFS_DEFAULTS_DIR_LIST. + rv = mGREDir->Clone(getter_AddRefs(file)); + if (NS_SUCCEEDED(rv)) { + rv = file->AppendNative("defaults"_ns); + if (NS_SUCCEEDED(rv)) rv = file->AppendNative("pref"_ns); + } +#endif + } else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_DIR) || + !strcmp(aProperty, XRE_USER_APP_DATA_DIR)) { + rv = GetUserAppDataDirectory(getter_AddRefs(file)); + } +#if defined(XP_UNIX) || defined(XP_MACOSX) + else if (!strcmp(aProperty, XRE_SYS_NATIVE_MANIFESTS)) { + nsCOMPtr<nsIFile> localDir; + + rv = ::GetSystemParentDirectory(getter_AddRefs(localDir)); + if (NS_SUCCEEDED(rv)) { + localDir.swap(file); + } + } else if (!strcmp(aProperty, XRE_USER_NATIVE_MANIFESTS)) { + nsCOMPtr<nsIFile> localDir; + rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false); + if (NS_SUCCEEDED(rv)) { +# if defined(XP_MACOSX) + rv = localDir->AppendNative("Mozilla"_ns); +# else + rv = localDir->AppendNative(".mozilla"_ns); +# endif + } + if (NS_SUCCEEDED(rv)) { + localDir.swap(file); + } + } +#endif + else if (!strcmp(aProperty, XRE_UPDATE_ROOT_DIR)) { + rv = GetUpdateRootDir(getter_AddRefs(file)); + } else if (!strcmp(aProperty, XRE_OLD_UPDATE_ROOT_DIR)) { + rv = GetUpdateRootDir(getter_AddRefs(file), true); + } else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_FILE)) { + rv = GetUserAppDataDirectory(getter_AddRefs(file)); + if (NS_SUCCEEDED(rv)) + rv = file->AppendNative(nsLiteralCString(APP_REGISTRY_NAME)); + } else if (!strcmp(aProperty, NS_APP_USER_PROFILES_ROOT_DIR)) { + rv = GetUserProfilesRootDir(getter_AddRefs(file)); + } else if (!strcmp(aProperty, NS_APP_USER_PROFILES_LOCAL_ROOT_DIR)) { + rv = GetUserProfilesLocalDir(getter_AddRefs(file)); + } else if (!strcmp(aProperty, XRE_EXECUTABLE_FILE)) { + nsCOMPtr<nsIFile> lf; + rv = XRE_GetBinaryPath(getter_AddRefs(lf)); + if (NS_SUCCEEDED(rv)) file = lf; + } + + else if (!strcmp(aProperty, NS_APP_PROFILE_DIR_STARTUP) && mProfileDir) { + return mProfileDir->Clone(aFile); + } else if (!strcmp(aProperty, NS_APP_PROFILE_LOCAL_DIR_STARTUP)) { + if (mProfileLocalDir) return mProfileLocalDir->Clone(aFile); + + if (mProfileDir) return mProfileDir->Clone(aFile); + + if (mAppProvider) + return mAppProvider->GetFile(NS_APP_PROFILE_DIR_STARTUP, aPersistent, + aFile); + } +#if defined(XP_UNIX) || defined(XP_MACOSX) + else if (!strcmp(aProperty, XRE_SYS_LOCAL_EXTENSION_PARENT_DIR)) { +# ifdef ENABLE_SYSTEM_EXTENSION_DIRS + return GetSystemExtensionsDirectory(aFile); +# else + return NS_ERROR_FAILURE; +# endif + } +#endif +#if defined(XP_UNIX) && !defined(XP_MACOSX) + else if (!strcmp(aProperty, XRE_SYS_SHARE_EXTENSION_PARENT_DIR)) { +# ifdef ENABLE_SYSTEM_EXTENSION_DIRS +# if defined(__OpenBSD__) || defined(__FreeBSD__) + static const char* const sysLExtDir = "/usr/local/share/mozilla/extensions"; +# else + static const char* const sysLExtDir = "/usr/share/mozilla/extensions"; +# endif + return NS_NewNativeLocalFile(nsDependentCString(sysLExtDir), false, aFile); +# else + return NS_ERROR_FAILURE; +# endif + } +#endif + else if (!strcmp(aProperty, XRE_USER_SYS_EXTENSION_DIR)) { +#ifdef ENABLE_SYSTEM_EXTENSION_DIRS + return GetSysUserExtensionsDirectory(aFile); +#else + return NS_ERROR_FAILURE; +#endif + } else if (!strcmp(aProperty, XRE_USER_SYS_EXTENSION_DEV_DIR)) { + return GetSysUserExtensionsDevDirectory(aFile); + } else if (!strcmp(aProperty, XRE_USER_RUNTIME_DIR)) { +#if defined(XP_UNIX) + nsPrintfCString path("/run/user/%d/%s/", getuid(), GetAppName()); + ToLowerCase(path); + return NS_NewNativeLocalFile(path, false, aFile); +#else + return NS_ERROR_FAILURE; +#endif + } else if (!strcmp(aProperty, XRE_APP_DISTRIBUTION_DIR)) { + bool persistent = false; + rv = GetFile(NS_GRE_DIR, &persistent, getter_AddRefs(file)); + if (NS_SUCCEEDED(rv)) rv = file->AppendNative("distribution"_ns); + } else if (!strcmp(aProperty, XRE_APP_FEATURES_DIR)) { + rv = GetAppDir()->Clone(getter_AddRefs(file)); + if (NS_SUCCEEDED(rv)) rv = file->AppendNative("features"_ns); + } else if (!strcmp(aProperty, XRE_ADDON_APP_DIR)) { + nsCOMPtr<nsIDirectoryServiceProvider> dirsvc( + do_GetService("@mozilla.org/file/directory_service;1", &rv)); + if (NS_FAILED(rv)) return rv; + bool unused; + rv = dirsvc->GetFile("XCurProcD", &unused, getter_AddRefs(file)); + } +#if defined(MOZ_SANDBOX) + else if (!strcmp(aProperty, NS_APP_CONTENT_PROCESS_TEMP_DIR)) { + if (!mContentTempDir && NS_FAILED((rv = LoadContentProcessTempDir()))) { + return rv; + } + rv = mContentTempDir->Clone(getter_AddRefs(file)); + } +#endif // defined(MOZ_SANDBOX) +#if defined(MOZ_SANDBOX) + else if (0 == strcmp(aProperty, NS_APP_PLUGIN_PROCESS_TEMP_DIR)) { + if (!mPluginTempDir && NS_FAILED((rv = LoadPluginProcessTempDir()))) { + return rv; + } + rv = mPluginTempDir->Clone(getter_AddRefs(file)); + } +#endif // defined(MOZ_SANDBOX) + else if (NS_SUCCEEDED(GetProfileStartupDir(getter_AddRefs(file)))) { + // We need to allow component, xpt, and chrome registration to + // occur prior to the profile-after-change notification. + if (!strcmp(aProperty, NS_APP_USER_CHROME_DIR)) { + rv = file->AppendNative("chrome"_ns); + } + } + + if (NS_SUCCEEDED(rv) && file) { + file.forget(aFile); + return NS_OK; + } + + bool ensureFilePermissions = false; + + if (NS_SUCCEEDED(GetProfileDir(getter_AddRefs(file)))) { + if (!strcmp(aProperty, NS_APP_PREFS_50_DIR)) { + rv = NS_OK; + } else if (!strcmp(aProperty, NS_APP_PREFS_50_FILE)) { + rv = file->AppendNative("prefs.js"_ns); + } else if (!strcmp(aProperty, NS_APP_PREFS_OVERRIDE_DIR)) { + rv = mProfileDir->Clone(getter_AddRefs(file)); + nsresult tmp = + file->AppendNative(nsLiteralCString(PREF_OVERRIDE_DIRNAME)); + if (NS_FAILED(tmp)) { + rv = tmp; + } + tmp = EnsureDirectoryExists(file); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + } + if (NS_FAILED(rv) || !file) return NS_ERROR_FAILURE; + + if (ensureFilePermissions) { + bool fileToEnsureExists; + bool isWritable; + if (NS_SUCCEEDED(file->Exists(&fileToEnsureExists)) && fileToEnsureExists && + NS_SUCCEEDED(file->IsWritable(&isWritable)) && !isWritable) { + uint32_t permissions; + if (NS_SUCCEEDED(file->GetPermissions(&permissions))) { + rv = file->SetPermissions(permissions | 0600); + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to ensure file permissions"); + } + } + } + + file.forget(aFile); + return NS_OK; +} + +static void LoadDirIntoArray(nsIFile* dir, const char* const* aAppendList, + nsCOMArray<nsIFile>& aDirectories) { + if (!dir) return; + + nsCOMPtr<nsIFile> subdir; + dir->Clone(getter_AddRefs(subdir)); + if (!subdir) return; + + for (const char* const* a = aAppendList; *a; ++a) { + subdir->AppendNative(nsDependentCString(*a)); + } + + bool exists; + if (NS_SUCCEEDED(subdir->Exists(&exists)) && exists) { + aDirectories.AppendObject(subdir); + } +} + +NS_IMETHODIMP +nsXREDirProvider::GetFiles(const char* aProperty, + nsISimpleEnumerator** aResult) { + nsresult rv; + + nsCOMPtr<nsISimpleEnumerator> appEnum; + nsCOMPtr<nsIDirectoryServiceProvider2> appP2(do_QueryInterface(mAppProvider)); + if (appP2) { + rv = appP2->GetFiles(aProperty, getter_AddRefs(appEnum)); + if (NS_FAILED(rv)) { + appEnum = nullptr; + } else if (rv != NS_SUCCESS_AGGREGATE_RESULT) { + appEnum.forget(aResult); + return NS_OK; + } + } + + nsCOMPtr<nsISimpleEnumerator> xreEnum; + rv = GetFilesInternal(aProperty, getter_AddRefs(xreEnum)); + if (NS_FAILED(rv)) { + if (appEnum) { + appEnum.forget(aResult); + return NS_SUCCESS_AGGREGATE_RESULT; + } + + return rv; + } + + rv = NS_NewUnionEnumerator(aResult, appEnum, xreEnum); + if (NS_FAILED(rv)) return rv; + + return NS_SUCCESS_AGGREGATE_RESULT; +} + +#if defined(MOZ_SANDBOX) + +static const char* GetProcessTempBaseDirKey() { +# if defined(XP_WIN) + return NS_WIN_LOW_INTEGRITY_TEMP_BASE; +# else + return NS_OS_TEMP_DIR; +# endif +} + +// +// Sets mContentTempDir so that it refers to the appropriate temp dir. +// If the sandbox is enabled, NS_APP_CONTENT_PROCESS_TEMP_DIR, otherwise +// NS_OS_TEMP_DIR is used. +// +nsresult nsXREDirProvider::LoadContentProcessTempDir() { + // The parent is responsible for creating the sandbox temp dir. + if (XRE_IsParentProcess()) { + mContentProcessSandboxTempDir = + CreateProcessSandboxTempDir(GeckoProcessType_Content); + mContentTempDir = mContentProcessSandboxTempDir; + } else { + mContentTempDir = !IsContentSandboxDisabled() + ? GetProcessSandboxTempDir(GeckoProcessType_Content) + : nullptr; + } + + if (!mContentTempDir) { + nsresult rv = + NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mContentTempDir)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + +# if defined(XP_WIN) + // The content temp dir can be used in sandbox rules, so we need to make sure + // it doesn't contain any junction points or symlinks or the sandbox will + // reject those rules. + if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks( + mContentTempDir)) { + NS_WARNING("Failed to resolve Content Temp Dir."); + } +# endif + + return NS_OK; +} + +// +// Sets mPluginTempDir so that it refers to the appropriate temp dir. +// If NS_APP_PLUGIN_PROCESS_TEMP_DIR fails for any reason, NS_OS_TEMP_DIR +// is used. +// +nsresult nsXREDirProvider::LoadPluginProcessTempDir() { + // The parent is responsible for creating the sandbox temp dir. + if (XRE_IsParentProcess()) { + mPluginProcessSandboxTempDir = + CreateProcessSandboxTempDir(GeckoProcessType_Plugin); + mPluginTempDir = mPluginProcessSandboxTempDir; + } else { + MOZ_ASSERT(XRE_IsPluginProcess()); + mPluginTempDir = GetProcessSandboxTempDir(GeckoProcessType_Plugin); + } + + if (!mPluginTempDir) { + nsresult rv = + NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mPluginTempDir)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + +# if defined(XP_WIN) + // The temp dir is used in sandbox rules, so we need to make sure + // it doesn't contain any junction points or symlinks or the sandbox will + // reject those rules. + if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks( + mPluginTempDir)) { + NS_WARNING("Failed to resolve plugin temp dir."); + } +# endif + + return NS_OK; +} + +static bool IsContentSandboxDisabled() { + return !mozilla::BrowserTabsRemoteAutostart() || + (!mozilla::IsContentSandboxEnabled()); +} + +// +// If a process sandbox temp dir is to be used, returns an nsIFile +// for the directory. Returns null if an error occurs. +// +static already_AddRefed<nsIFile> GetProcessSandboxTempDir( + GeckoProcessType type) { + nsCOMPtr<nsIFile> localFile; + + nsresult rv = NS_GetSpecialDirectory(GetProcessTempBaseDirKey(), + getter_AddRefs(localFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + MOZ_ASSERT((type == GeckoProcessType_Content) || + (type == GeckoProcessType_Plugin)); + + const char* prefKey = (type == GeckoProcessType_Content) + ? "security.sandbox.content.tempDirSuffix" + : "security.sandbox.plugin.tempDirSuffix"; + + nsAutoString tempDirSuffix; + rv = mozilla::Preferences::GetString(prefKey, tempDirSuffix); + if (NS_WARN_IF(NS_FAILED(rv)) || tempDirSuffix.IsEmpty()) { + return nullptr; + } + + rv = localFile->Append(u"Temp-"_ns + tempDirSuffix); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + return localFile.forget(); +} + +// +// Create a temporary directory for use from sandboxed processes. +// Only called in the parent. The path is derived from a UUID stored in a +// pref which is available to content and plugin processes. Returns null +// if the content sandbox is disabled or if an error occurs. +// +static already_AddRefed<nsIFile> CreateProcessSandboxTempDir( + GeckoProcessType procType) { + if ((procType == GeckoProcessType_Content) && IsContentSandboxDisabled()) { + return nullptr; + } + + MOZ_ASSERT((procType == GeckoProcessType_Content) || + (procType == GeckoProcessType_Plugin)); + + // Get (and create if blank) temp directory suffix pref. + const char* pref = (procType == GeckoProcessType_Content) + ? "security.sandbox.content.tempDirSuffix" + : "security.sandbox.plugin.tempDirSuffix"; + + nsresult rv; + nsAutoString tempDirSuffix; + mozilla::Preferences::GetString(pref, tempDirSuffix); + if (tempDirSuffix.IsEmpty()) { + nsCOMPtr<nsIUUIDGenerator> uuidgen = + do_GetService("@mozilla.org/uuid-generator;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + nsID uuid; + rv = uuidgen->GenerateUUIDInPlace(&uuid); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + char uuidChars[NSID_LENGTH]; + uuid.ToProvidedString(uuidChars); + tempDirSuffix.AssignASCII(uuidChars, NSID_LENGTH); +# ifdef XP_UNIX + // Braces in a path are somewhat annoying to deal with + // and pretty alien on Unix + tempDirSuffix.StripChars(u"{}"); +# endif + + // Save the pref + rv = mozilla::Preferences::SetString(pref, tempDirSuffix); + if (NS_WARN_IF(NS_FAILED(rv))) { + // If we fail to save the pref we don't want to create the temp dir, + // because we won't be able to clean it up later. + return nullptr; + } + + nsCOMPtr<nsIPrefService> prefsvc = mozilla::Preferences::GetService(); + if (!prefsvc || NS_FAILED((rv = prefsvc->SavePrefFile(nullptr)))) { + // Again, if we fail to save the pref file we might not be able to clean + // up the temp directory, so don't create one. Note that in the case + // the preference values allows an off main thread save, the successful + // return from the call doesn't mean we actually saved the file. See + // bug 1364496 for details. + NS_WARNING("Failed to save pref file, cannot create temp dir."); + return nullptr; + } + } + + nsCOMPtr<nsIFile> sandboxTempDir = GetProcessSandboxTempDir(procType); + if (!sandboxTempDir) { + NS_WARNING("Failed to determine sandbox temp dir path."); + return nullptr; + } + + // Remove the directory. It may exist due to a previous crash. + if (NS_FAILED(DeleteDirIfExists(sandboxTempDir))) { + NS_WARNING("Failed to reset sandbox temp dir."); + return nullptr; + } + + // Create the directory + rv = sandboxTempDir->Create(nsIFile::DIRECTORY_TYPE, 0700); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to create sandbox temp dir."); + return nullptr; + } + + return sandboxTempDir.forget(); +} + +static nsresult DeleteDirIfExists(nsIFile* dir) { + if (dir) { + // Don't return an error if the directory doesn't exist. + // Windows Remove() returns NS_ERROR_FILE_NOT_FOUND while + // OS X returns NS_ERROR_FILE_TARGET_DOES_NOT_EXIST. + nsresult rv = dir->Remove(/* aRecursive */ true); + if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND && + rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) { + return rv; + } + } + return NS_OK; +} + +#endif // defined(MOZ_SANDBOX) + +static const char* const kAppendPrefDir[] = {"defaults", "preferences", + nullptr}; + +nsresult nsXREDirProvider::GetFilesInternal(const char* aProperty, + nsISimpleEnumerator** aResult) { + nsresult rv = NS_OK; + *aResult = nullptr; + + if (!strcmp(aProperty, NS_APP_PREFS_DEFAULTS_DIR_LIST)) { + nsCOMArray<nsIFile> directories; + + LoadDirIntoArray(mXULAppDir, kAppendPrefDir, directories); + + rv = NS_NewArrayEnumerator(aResult, directories, NS_GET_IID(nsIFile)); + } else if (!strcmp(aProperty, NS_APP_CHROME_DIR_LIST)) { + // NS_APP_CHROME_DIR_LIST is only used to get default (native) icons + // for OS window decoration. + + static const char* const kAppendChromeDir[] = {"chrome", nullptr}; + nsCOMArray<nsIFile> directories; + LoadDirIntoArray(mXULAppDir, kAppendChromeDir, directories); + + rv = NS_NewArrayEnumerator(aResult, directories, NS_GET_IID(nsIFile)); + } else + rv = NS_ERROR_FAILURE; + + return rv; +} + +NS_IMETHODIMP +nsXREDirProvider::GetDirectory(nsIFile** aResult) { + NS_ENSURE_TRUE(mProfileDir, NS_ERROR_NOT_INITIALIZED); + + return mProfileDir->Clone(aResult); +} + +void nsXREDirProvider::InitializeUserPrefs() { + if (!mPrefsInitialized) { + // Temporarily set mProfileNotified to true so that the preference service + // can access the profile directory during initialization. Afterwards, clear + // it so that no other code can inadvertently access it until we get to + // profile-do-change. + mozilla::AutoRestore<bool> ar(mProfileNotified); + mProfileNotified = true; + + mozilla::Preferences::InitializeUserPrefs(); + } +} + +void nsXREDirProvider::FinishInitializingUserPrefs() { + if (!mPrefsInitialized) { + // See InitializeUserPrefs above. + mozilla::AutoRestore<bool> ar(mProfileNotified); + mProfileNotified = true; + + mozilla::Preferences::FinishInitializingUserPrefs(); + + mPrefsInitialized = true; + } +} + +NS_IMETHODIMP +nsXREDirProvider::DoStartup() { + nsresult rv; + + if (!mProfileNotified) { + nsCOMPtr<nsIObserverService> obsSvc = + mozilla::services::GetObserverService(); + if (!obsSvc) return NS_ERROR_FAILURE; + + mProfileNotified = true; + + /* + Make sure we've setup prefs before profile-do-change to be able to use + them to track crashes and because we want to begin crash tracking before + other code run from this notification since they may cause crashes. + */ + MOZ_ASSERT(mPrefsInitialized); + + bool safeModeNecessary = false; + nsCOMPtr<nsIAppStartup> appStartup( + mozilla::components::AppStartup::Service()); + if (appStartup) { + rv = appStartup->TrackStartupCrashBegin(&safeModeNecessary); + if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) + NS_WARNING("Error while beginning startup crash tracking"); + + if (!gSafeMode && safeModeNecessary) { + appStartup->RestartInSafeMode(nsIAppStartup::eForceQuit); + return NS_OK; + } + } + + static const char16_t kStartup[] = {'s', 't', 'a', 'r', + 't', 'u', 'p', '\0'}; + obsSvc->NotifyObservers(nullptr, "profile-do-change", kStartup); + + // Initialize the Enterprise Policies service in the parent process + // In the content process it's loaded on demand when needed + if (XRE_IsParentProcess()) { + nsCOMPtr<nsIObserver> policies( + do_GetService("@mozilla.org/enterprisepolicies;1")); + if (policies) { + policies->Observe(nullptr, "policies-startup", nullptr); + } + } + +#if defined(MOZ_SANDBOX) && defined(XP_WIN) + // Call SandboxBroker to initialize things that depend on Gecko machinery + // like the directory provider. We insert this initialization code here + // (rather than in XRE_mainRun) because we need NS_APP_USER_PROFILE_50_DIR + // to be known and so that any child content processes spawned by extensions + // from the notifications below will have all the requisite directories + // white-listed for read/write access. An example of this is the + // tor-launcher launching the network configuration window. See bug 1485836. + mozilla::SandboxBroker::GeckoDependentInitialize(); +#endif + + // Init the Extension Manager + nsCOMPtr<nsIObserver> em = + do_GetService("@mozilla.org/addons/integration;1"); + if (em) { + em->Observe(nullptr, "addons-startup", nullptr); + } else { + NS_WARNING("Failed to create Addons Manager."); + } + + obsSvc->NotifyObservers(nullptr, "profile-after-change", kStartup); + + // Any component that has registered for the profile-after-change category + // should also be created at this time. + (void)NS_CreateServicesFromCategory("profile-after-change", nullptr, + "profile-after-change"); + + if (gSafeMode && safeModeNecessary) { + static const char16_t kCrashed[] = {'c', 'r', 'a', 's', + 'h', 'e', 'd', '\0'}; + obsSvc->NotifyObservers(nullptr, "safemode-forced", kCrashed); + } + + // 1 = Regular mode, 2 = Safe mode, 3 = Safe mode forced + int mode = 1; + if (gSafeMode) { + if (safeModeNecessary) + mode = 3; + else + mode = 2; + } + mozilla::Telemetry::Accumulate(mozilla::Telemetry::SAFE_MODE_USAGE, mode); + + // Telemetry about number of profiles. + nsCOMPtr<nsIToolkitProfileService> profileService = + do_GetService("@mozilla.org/toolkit/profile-service;1"); + if (profileService) { + uint32_t count = 0; + rv = profileService->GetProfileCount(&count); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mozilla::Telemetry::Accumulate(mozilla::Telemetry::NUMBER_OF_PROFILES, + count); + } + + obsSvc->NotifyObservers(nullptr, "profile-initial-state", nullptr); + +#if defined(MOZ_SANDBOX) + // Makes sure the content temp dir has been loaded if it hasn't been + // already. In the parent this ensures it has been created before we attempt + // to start any content processes. + if (!mContentTempDir) { + mozilla::Unused << NS_WARN_IF(NS_FAILED(LoadContentProcessTempDir())); + } +#endif +#if defined(MOZ_SANDBOX) + if (!mPluginTempDir) { + mozilla::Unused << NS_WARN_IF(NS_FAILED(LoadPluginProcessTempDir())); + } +#endif + } + return NS_OK; +} + +void nsXREDirProvider::DoShutdown() { + AUTO_PROFILER_LABEL("nsXREDirProvider::DoShutdown", OTHER); + + if (mProfileNotified) { + nsCOMPtr<nsIObserverService> obsSvc = + mozilla::services::GetObserverService(); + NS_ASSERTION(obsSvc, "No observer service?"); + if (obsSvc) { + static const char16_t kShutdownPersist[] = u"shutdown-persist"; + obsSvc->NotifyObservers(nullptr, "profile-change-net-teardown", + kShutdownPersist); + obsSvc->NotifyObservers(nullptr, "profile-change-teardown", + kShutdownPersist); + +#ifdef DEBUG + // Not having this causes large intermittent leaks. See bug 1340425. + if (JSContext* cx = mozilla::dom::danger::GetJSContext()) { + JS_GC(cx); + } +#endif + + obsSvc->NotifyObservers(nullptr, "profile-before-change", + kShutdownPersist); + obsSvc->NotifyObservers(nullptr, "profile-before-change-qm", + kShutdownPersist); + obsSvc->NotifyObservers(nullptr, "profile-before-change-telemetry", + kShutdownPersist); + } + mProfileNotified = false; + } + + gDataDirProfileLocal = nullptr; + gDataDirProfile = nullptr; + + if (XRE_IsParentProcess()) { +#if defined(MOZ_SANDBOX) + mozilla::Unused << DeleteDirIfExists(mContentProcessSandboxTempDir); + mozilla::Unused << DeleteDirIfExists(mPluginProcessSandboxTempDir); +#endif + } +} + +#ifdef XP_WIN +static nsresult GetShellFolderPath(KNOWNFOLDERID folder, nsAString& _retval) { + DWORD flags = KF_FLAG_SIMPLE_IDLIST | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_ALIAS; + PWSTR path = nullptr; + + if (!SUCCEEDED(SHGetKnownFolderPath(folder, flags, NULL, &path))) { + return NS_ERROR_NOT_AVAILABLE; + } + + _retval = nsDependentString(path); + CoTaskMemFree(path); + return NS_OK; +} + +/** + * Provides a fallback for getting the path to APPDATA or LOCALAPPDATA by + * querying the registry when the call to SHGetSpecialFolderLocation or + * SHGetPathFromIDListW is unable to provide these paths (Bug 513958). + */ +static nsresult GetRegWindowsAppDataFolder(bool aLocal, nsAString& _retval) { + HKEY key; + LPCWSTR keyName = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"; + DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, keyName, 0, KEY_READ, &key); + if (res != ERROR_SUCCESS) { + _retval.SetLength(0); + return NS_ERROR_NOT_AVAILABLE; + } + + DWORD type, size; + res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"), nullptr, + &type, nullptr, &size); + // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the + // buffer size must not equal 0, and the buffer size be a multiple of 2. + if (res != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0) { + ::RegCloseKey(key); + _retval.SetLength(0); + return NS_ERROR_NOT_AVAILABLE; + } + + // |size| may or may not include room for the terminating null character + DWORD resultLen = size / 2; + + if (!_retval.SetLength(resultLen, mozilla::fallible)) { + ::RegCloseKey(key); + _retval.SetLength(0); + return NS_ERROR_NOT_AVAILABLE; + } + + auto begin = _retval.BeginWriting(); + + res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"), nullptr, + nullptr, (LPBYTE)begin, &size); + ::RegCloseKey(key); + if (res != ERROR_SUCCESS) { + _retval.SetLength(0); + return NS_ERROR_NOT_AVAILABLE; + } + + if (!_retval.CharAt(resultLen - 1)) { + // It was already null terminated. + _retval.Truncate(resultLen - 1); + } + + return NS_OK; +} +#endif + +static nsresult HashInstallPath(nsAString& aInstallPath, nsAString& aPathHash) { + const char* vendor = GetAppVendor(); + if (vendor && vendor[0] == '\0') { + vendor = nullptr; + } + + mozilla::UniquePtr<NS_tchar[]> hash; + bool success = + ::GetInstallHash(PromiseFlatString(aInstallPath).get(), vendor, hash); + if (!success) { + return NS_ERROR_FAILURE; + } + + // The hash string is a NS_tchar*, which is wchar* in Windows and char* + // elsewhere. +#ifdef XP_WIN + aPathHash.Assign(hash.get()); +#else + aPathHash.AssignASCII(hash.get()); +#endif + return NS_OK; +} + +/** + * Gets a hash of the installation directory. + */ +nsresult nsXREDirProvider::GetInstallHash(nsAString& aPathHash) { + nsCOMPtr<nsIFile> installDir; + nsCOMPtr<nsIFile> appFile; + bool per = false; + nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = appFile->GetParent(getter_AddRefs(installDir)); + NS_ENSURE_SUCCESS(rv, rv); + + // It is possible that the path we have is on a case insensitive + // filesystem in which case the path may vary depending on how the + // application is called. We want to normalize the case somehow. +#ifdef XP_WIN + // Windows provides a way to get the correct case. + if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks( + installDir)) { + NS_WARNING("Failed to resolve install directory."); + } +#elif defined(MOZ_WIDGET_COCOA) + // On OSX roundtripping through an FSRef fixes the case. + FSRef ref; + nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(installDir); + rv = macFile->GetFSRef(&ref); + NS_ENSURE_SUCCESS(rv, rv); + rv = NS_NewLocalFileWithFSRef(&ref, true, getter_AddRefs(macFile)); + NS_ENSURE_SUCCESS(rv, rv); + installDir = static_cast<nsIFile*>(macFile); +#endif + // On linux XRE_EXECUTABLE_FILE already seems to be set to the correct path. + + nsAutoString installPath; + rv = installDir->GetPath(installPath); + NS_ENSURE_SUCCESS(rv, rv); + + return HashInstallPath(installPath, aPathHash); +} + +/** + * Before bug 1555319 the directory hashed can have had an incorrect case. + * Access to that hash is still available through this function. It is needed so + * we can migrate users who may have an incorrect hash in profiles.ini. This + * support can probably be removed in a few releases time. + */ +nsresult nsXREDirProvider::GetLegacyInstallHash(nsAString& aPathHash) { + nsCOMPtr<nsIFile> installDir; + nsCOMPtr<nsIFile> appFile; + bool per = false; + nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = appFile->GetParent(getter_AddRefs(installDir)); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString installPath; + rv = installDir->GetPath(installPath); + NS_ENSURE_SUCCESS(rv, rv); + +#ifdef XP_WIN +# if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE) + // Convert a 64-bit install path to what would have been the 32-bit install + // path to allow users to migrate their profiles from one to the other. + PWSTR pathX86 = nullptr; + HRESULT hres = + SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, 0, nullptr, &pathX86); + if (SUCCEEDED(hres)) { + nsDependentString strPathX86(pathX86); + if (!StringBeginsWith(installPath, strPathX86, + nsCaseInsensitiveStringComparator)) { + PWSTR path = nullptr; + hres = SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, nullptr, &path); + if (SUCCEEDED(hres)) { + if (StringBeginsWith(installPath, nsDependentString(path), + nsCaseInsensitiveStringComparator)) { + installPath.Replace(0, wcslen(path), strPathX86); + } + } + CoTaskMemFree(path); + } + } + CoTaskMemFree(pathX86); +# endif +#endif + return HashInstallPath(installPath, aPathHash); +} + +nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult, + bool aGetOldLocation) { +#ifndef XP_WIN + // There is no old update location on platforms other than Windows. Windows is + // the only platform for which we migrated the update directory. + if (aGetOldLocation) { + return NS_ERROR_NOT_IMPLEMENTED; + } +#endif + nsCOMPtr<nsIFile> updRoot; + nsCOMPtr<nsIFile> appFile; + bool per = false; + nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = appFile->GetParent(getter_AddRefs(updRoot)); + NS_ENSURE_SUCCESS(rv, rv); + +#ifdef XP_MACOSX + nsCOMPtr<nsIFile> appRootDirFile; + nsCOMPtr<nsIFile> localDir; + nsAutoString appDirPath; + if (NS_FAILED(appFile->GetParent(getter_AddRefs(appRootDirFile))) || + NS_FAILED(appRootDirFile->GetPath(appDirPath)) || + NS_FAILED(GetUserDataDirectoryHome(getter_AddRefs(localDir), true))) { + return NS_ERROR_FAILURE; + } + + int32_t dotIndex = appDirPath.RFind(".app"); + if (dotIndex == kNotFound) { + dotIndex = appDirPath.Length(); + } + appDirPath = Substring(appDirPath, 1, dotIndex - 1); + + bool hasVendor = GetAppVendor() && strlen(GetAppVendor()) != 0; + if (hasVendor || GetAppName()) { + if (NS_FAILED(localDir->AppendNative( + nsDependentCString(hasVendor ? GetAppVendor() : GetAppName())))) { + return NS_ERROR_FAILURE; + } + } else if (NS_FAILED(localDir->AppendNative("Mozilla"_ns))) { + return NS_ERROR_FAILURE; + } + + if (NS_FAILED(localDir->Append(u"updates"_ns)) || + NS_FAILED(localDir->AppendRelativePath(appDirPath))) { + return NS_ERROR_FAILURE; + } + + localDir.forget(aResult); + return NS_OK; + +#elif XP_WIN + nsAutoString installPath; + rv = updRoot->GetPath(installPath); + NS_ENSURE_SUCCESS(rv, rv); + + mozilla::UniquePtr<wchar_t[]> updatePath; + HRESULT hrv; + if (aGetOldLocation) { + const char* vendor = GetAppVendor(); + if (vendor && vendor[0] == '\0') { + vendor = nullptr; + } + const char* appName = GetAppName(); + if (appName && appName[0] == '\0') { + appName = nullptr; + } + hrv = GetUserUpdateDirectory(PromiseFlatString(installPath).get(), vendor, + appName, updatePath); + } else { + hrv = GetCommonUpdateDirectory(PromiseFlatString(installPath).get(), + SetPermissionsOf::BaseDirIfNotExists, + updatePath); + } + if (FAILED(hrv)) { + return NS_ERROR_FAILURE; + } + nsAutoString updatePathStr; + updatePathStr.Assign(updatePath.get()); + updRoot->InitWithPath(updatePathStr); +#endif // XP_WIN + updRoot.forget(aResult); + return NS_OK; +} + +nsresult nsXREDirProvider::GetProfileStartupDir(nsIFile** aResult) { + if (mProfileDir) return mProfileDir->Clone(aResult); + + if (mAppProvider) { + nsCOMPtr<nsIFile> needsclone; + bool dummy; + nsresult rv = mAppProvider->GetFile(NS_APP_PROFILE_DIR_STARTUP, &dummy, + getter_AddRefs(needsclone)); + if (NS_SUCCEEDED(rv)) return needsclone->Clone(aResult); + } + + return NS_ERROR_FAILURE; +} + +nsresult nsXREDirProvider::GetProfileDir(nsIFile** aResult) { + if (mProfileDir) { + if (!mProfileNotified) return NS_ERROR_FAILURE; + + return mProfileDir->Clone(aResult); + } + + if (mAppProvider) { + nsCOMPtr<nsIFile> needsclone; + bool dummy; + nsresult rv = mAppProvider->GetFile(NS_APP_USER_PROFILE_50_DIR, &dummy, + getter_AddRefs(needsclone)); + if (NS_SUCCEEDED(rv)) return needsclone->Clone(aResult); + } + + return NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, aResult); +} + +NS_IMETHODIMP +nsXREDirProvider::SetUserDataDirectory(nsIFile* aFile, bool aLocal) { + if (aLocal) { + NS_IF_RELEASE(gDataDirHomeLocal); + NS_IF_ADDREF(gDataDirHomeLocal = aFile); + } else { + NS_IF_RELEASE(gDataDirHome); + NS_IF_ADDREF(gDataDirHome = aFile); + } + + return NS_OK; +} + +/* static */ +nsresult nsXREDirProvider::SetUserDataProfileDirectory(nsCOMPtr<nsIFile>& aFile, + bool aLocal) { + if (aLocal) { + gDataDirProfileLocal = aFile; + } else { + gDataDirProfile = aFile; + } + + return NS_OK; +} + +nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, + bool aLocal) { + // Copied from nsAppFileLocationProvider (more or less) + nsresult rv; + nsCOMPtr<nsIFile> localDir; + + if (aLocal && gDataDirHomeLocal) { + return gDataDirHomeLocal->Clone(aFile); + } + if (!aLocal && gDataDirHome) { + return gDataDirHome->Clone(aFile); + } + +#if defined(XP_MACOSX) + FSRef fsRef; + OSType folderType; + if (aLocal) { + folderType = kCachedDataFolderType; + } else { +# ifdef MOZ_THUNDERBIRD + folderType = kDomainLibraryFolderType; +# else + folderType = kApplicationSupportFolderType; +# endif + } + OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef); + NS_ENSURE_FALSE(err, NS_ERROR_FAILURE); + + rv = NS_NewNativeLocalFile(""_ns, true, getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsILocalFileMac> dirFileMac = do_QueryInterface(localDir); + NS_ENSURE_TRUE(dirFileMac, NS_ERROR_UNEXPECTED); + + rv = dirFileMac->InitWithFSRef(&fsRef); + NS_ENSURE_SUCCESS(rv, rv); + + localDir = dirFileMac; +#elif defined(XP_IOS) + nsAutoCString userDir; + if (GetUIKitDirectory(aLocal, userDir)) { + rv = NS_NewNativeLocalFile(userDir, true, getter_AddRefs(localDir)); + } else { + rv = NS_ERROR_FAILURE; + } + NS_ENSURE_SUCCESS(rv, rv); +#elif defined(XP_WIN) + nsString path; + if (aLocal) { + rv = GetShellFolderPath(FOLDERID_LocalAppData, path); + if (NS_FAILED(rv)) rv = GetRegWindowsAppDataFolder(aLocal, path); + } + if (!aLocal || NS_FAILED(rv)) { + rv = GetShellFolderPath(FOLDERID_RoamingAppData, path); + if (NS_FAILED(rv)) { + if (!aLocal) rv = GetRegWindowsAppDataFolder(aLocal, path); + } + } + NS_ENSURE_SUCCESS(rv, rv); + + rv = NS_NewLocalFile(path, true, getter_AddRefs(localDir)); +#elif defined(XP_UNIX) + const char* homeDir = getenv("HOME"); + if (!homeDir || !*homeDir) return NS_ERROR_FAILURE; + +# ifdef ANDROID /* We want (ProfD == ProfLD) on Android. */ + aLocal = false; +# endif + + if (aLocal) { + // If $XDG_CACHE_HOME is defined use it, otherwise use $HOME/.cache. + const char* cacheHome = getenv("XDG_CACHE_HOME"); + if (cacheHome && *cacheHome) { + rv = NS_NewNativeLocalFile(nsDependentCString(cacheHome), true, + getter_AddRefs(localDir)); + } else { + rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true, + getter_AddRefs(localDir)); + if (NS_SUCCEEDED(rv)) rv = localDir->AppendNative(".cache"_ns); + } + } else { + rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true, + getter_AddRefs(localDir)); + } +#else +# error "Don't know how to get product dir on your platform" +#endif + + NS_IF_ADDREF(*aFile = localDir); + return rv; +} + +nsresult nsXREDirProvider::GetSysUserExtensionsDirectory(nsIFile** aFile) { + nsCOMPtr<nsIFile> localDir; + nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AppendSysUserExtensionPath(localDir); + NS_ENSURE_SUCCESS(rv, rv); + + rv = EnsureDirectoryExists(localDir); + NS_ENSURE_SUCCESS(rv, rv); + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + // This is used in sandbox rules, so we need to make sure it doesn't contain + // any junction points or symlinks or the sandbox will reject those rules. + if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks(localDir)) { + NS_WARNING("Failed to resolve sys user extensions directory."); + } +#endif + + localDir.forget(aFile); + return NS_OK; +} + +nsresult nsXREDirProvider::GetSysUserExtensionsDevDirectory(nsIFile** aFile) { + nsCOMPtr<nsIFile> localDir; + nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AppendSysUserExtensionsDevPath(localDir); + NS_ENSURE_SUCCESS(rv, rv); + + rv = EnsureDirectoryExists(localDir); + NS_ENSURE_SUCCESS(rv, rv); + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + // This is used in sandbox rules, so we need to make sure it doesn't contain + // any junction points or symlinks or the sandbox will reject those rules. + if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks(localDir)) { + NS_WARNING("Failed to resolve sys user extensions dev directory."); + } +#endif + + localDir.forget(aFile); + return NS_OK; +} + +#if defined(XP_UNIX) || defined(XP_MACOSX) +nsresult nsXREDirProvider::GetSystemExtensionsDirectory(nsIFile** aFile) { + nsresult rv; + nsCOMPtr<nsIFile> localDir; + + rv = GetSystemParentDirectory(getter_AddRefs(localDir)); + if (NS_SUCCEEDED(rv)) { + constexpr auto sExtensions = +# if defined(XP_MACOSX) + "Extensions"_ns +# else + "extensions"_ns +# endif + ; + + rv = localDir->AppendNative(sExtensions); + if (NS_SUCCEEDED(rv)) { + localDir.forget(aFile); + } + } + return rv; +} +#endif + +nsresult nsXREDirProvider::GetUserDataDirectory(nsIFile** aFile, bool aLocal) { + nsCOMPtr<nsIFile> localDir; + + if (aLocal && gDataDirProfileLocal) { + return gDataDirProfileLocal->Clone(aFile); + } + if (!aLocal && gDataDirProfile) { + return gDataDirProfile->Clone(aFile); + } + + nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), aLocal); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AppendProfilePath(localDir, aLocal); + NS_ENSURE_SUCCESS(rv, rv); + + rv = EnsureDirectoryExists(localDir); + NS_ENSURE_SUCCESS(rv, rv); + + nsXREDirProvider::SetUserDataProfileDirectory(localDir, aLocal); + + localDir.forget(aFile); + return NS_OK; +} + +nsresult nsXREDirProvider::EnsureDirectoryExists(nsIFile* aDirectory) { + nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0700); + + if (rv == NS_ERROR_FILE_ALREADY_EXISTS) { + rv = NS_OK; + } + return rv; +} + +nsresult nsXREDirProvider::AppendSysUserExtensionPath(nsIFile* aFile) { + NS_ASSERTION(aFile, "Null pointer!"); + + nsresult rv; + +#if defined(XP_MACOSX) || defined(XP_WIN) + + static const char* const sXR = "Mozilla"; + rv = aFile->AppendNative(nsDependentCString(sXR)); + NS_ENSURE_SUCCESS(rv, rv); + + static const char* const sExtensions = "Extensions"; + rv = aFile->AppendNative(nsDependentCString(sExtensions)); + NS_ENSURE_SUCCESS(rv, rv); + +#elif defined(XP_UNIX) + + static const char* const sXR = ".mozilla"; + rv = aFile->AppendNative(nsDependentCString(sXR)); + NS_ENSURE_SUCCESS(rv, rv); + + static const char* const sExtensions = "extensions"; + rv = aFile->AppendNative(nsDependentCString(sExtensions)); + NS_ENSURE_SUCCESS(rv, rv); + +#else +# error "Don't know how to get XRE user extension path on your platform" +#endif + return NS_OK; +} + +nsresult nsXREDirProvider::AppendSysUserExtensionsDevPath(nsIFile* aFile) { + MOZ_ASSERT(aFile); + + nsresult rv; + +#if defined(XP_MACOSX) || defined(XP_WIN) + + static const char* const sXR = "Mozilla"; + rv = aFile->AppendNative(nsDependentCString(sXR)); + NS_ENSURE_SUCCESS(rv, rv); + + static const char* const sExtensions = "SystemExtensionsDev"; + rv = aFile->AppendNative(nsDependentCString(sExtensions)); + NS_ENSURE_SUCCESS(rv, rv); + +#elif defined(XP_UNIX) + + static const char* const sXR = ".mozilla"; + rv = aFile->AppendNative(nsDependentCString(sXR)); + NS_ENSURE_SUCCESS(rv, rv); + + static const char* const sExtensions = "systemextensionsdev"; + rv = aFile->AppendNative(nsDependentCString(sExtensions)); + NS_ENSURE_SUCCESS(rv, rv); + +#else +# error "Don't know how to get XRE system extension dev path on your platform" +#endif + return NS_OK; +} + +nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) { + NS_ASSERTION(aFile, "Null pointer!"); + + // If there is no XREAppData then there is no information to use to build + // the profile path so just do nothing. This should only happen in xpcshell + // tests. + if (!gAppData) { + return NS_OK; + } + + nsAutoCString profile; + nsAutoCString appName; + nsAutoCString vendor; + if (gAppData->profile) { + profile = gAppData->profile; + } else { + appName = gAppData->name; + vendor = gAppData->vendor; + } + + nsresult rv = NS_OK; + +#if defined(XP_MACOSX) + if (!profile.IsEmpty()) { + rv = AppendProfileString(aFile, profile.get()); + } else { + // Note that MacOS ignores the vendor when creating the profile hierarchy - + // all application preferences directories live alongside one another in + // ~/Library/Application Support/ + rv = aFile->AppendNative(appName); + } + NS_ENSURE_SUCCESS(rv, rv); + +#elif defined(XP_WIN) + if (!profile.IsEmpty()) { + rv = AppendProfileString(aFile, profile.get()); + } else { + if (!vendor.IsEmpty()) { + rv = aFile->AppendNative(vendor); + NS_ENSURE_SUCCESS(rv, rv); + } + rv = aFile->AppendNative(appName); + } + NS_ENSURE_SUCCESS(rv, rv); + +#elif defined(ANDROID) + // The directory used for storing profiles + // The parent of this directory is set in GetUserDataDirectoryHome + // XXX: handle gAppData->profile properly + // XXXsmaug ...and the rest of the profile creation! + rv = aFile->AppendNative(nsDependentCString("mozilla")); + NS_ENSURE_SUCCESS(rv, rv); +#elif defined(XP_UNIX) + nsAutoCString folder; + // Make it hidden (by starting with "."), except when local (the + // profile is already under ~/.cache or XDG_CACHE_HOME). + if (!aLocal) folder.Assign('.'); + + if (!profile.IsEmpty()) { + // Skip any leading path characters + const char* profileStart = profile.get(); + while (*profileStart == '/' || *profileStart == '\\') profileStart++; + + // On the off chance that someone wanted their folder to be hidden don't + // let it become ".." + if (*profileStart == '.' && !aLocal) profileStart++; + + folder.Append(profileStart); + ToLowerCase(folder); + + rv = AppendProfileString(aFile, folder.BeginReading()); + } else { + if (!vendor.IsEmpty()) { + folder.Append(vendor); + ToLowerCase(folder); + + rv = aFile->AppendNative(folder); + NS_ENSURE_SUCCESS(rv, rv); + + folder.Truncate(); + } + + // This can be the case in tests. + if (!appName.IsEmpty()) { + folder.Append(appName); + ToLowerCase(folder); + + rv = aFile->AppendNative(folder); + } + } + NS_ENSURE_SUCCESS(rv, rv); + +#else +# error "Don't know how to get profile path on your platform" +#endif + return NS_OK; +} + +nsresult nsXREDirProvider::AppendProfileString(nsIFile* aFile, + const char* aPath) { + NS_ASSERTION(aFile, "Null file!"); + NS_ASSERTION(aPath, "Null path!"); + + nsAutoCString pathDup(aPath); + + char* path = pathDup.BeginWriting(); + + nsresult rv; + char* subdir; + while ((subdir = NS_strtok("/\\", &path))) { + rv = aFile->AppendNative(nsDependentCString(subdir)); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} |