1 /* -*- Mode: C++; tab-width: 2; 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 "nsAppRunner.h"
7 #include "nsXREDirProvider.h"
8 #ifndef ANDROID
9 #  include "commonupdatedir.h"
10 #endif
11 
12 #include "jsapi.h"
13 #include "xpcpublic.h"
14 
15 #include "nsIAppStartup.h"
16 #include "nsIFile.h"
17 #include "nsIObserver.h"
18 #include "nsIObserverService.h"
19 #include "nsISimpleEnumerator.h"
20 #include "nsIToolkitProfileService.h"
21 #include "nsIXULRuntime.h"
22 #include "commonupdatedir.h"
23 
24 #include "nsAppDirectoryServiceDefs.h"
25 #include "nsDirectoryServiceDefs.h"
26 #include "nsDirectoryServiceUtils.h"
27 #include "nsXULAppAPI.h"
28 #include "nsCategoryManagerUtils.h"
29 
30 #include "nsDependentString.h"
31 #include "nsCOMArray.h"
32 #include "nsArrayEnumerator.h"
33 #include "nsEnumeratorUtils.h"
34 #include "nsReadableUtils.h"
35 
36 #include "SpecialSystemDirectory.h"
37 
38 #include "mozilla/dom/ScriptSettings.h"
39 
40 #include "mozilla/AppShutdown.h"
41 #include "mozilla/AutoRestore.h"
42 #ifdef MOZ_BACKGROUNDTASKS
43 #  include "mozilla/BackgroundTasks.h"
44 #endif
45 #include "mozilla/Components.h"
46 #include "mozilla/Services.h"
47 #include "mozilla/Omnijar.h"
48 #include "mozilla/Preferences.h"
49 #include "mozilla/ProfilerLabels.h"
50 #include "mozilla/Telemetry.h"
51 #include "mozilla/XREAppData.h"
52 #include "nsPrintfCString.h"
53 
54 #ifdef MOZ_THUNDERBIRD
55 #  include "nsIPK11TokenDB.h"
56 #  include "nsIPK11Token.h"
57 #endif
58 
59 #include <stdlib.h>
60 
61 #ifdef XP_WIN
62 #  include <windows.h>
63 #  include <shlobj.h>
64 #  include "WinUtils.h"
65 #endif
66 #ifdef XP_MACOSX
67 #  include "nsILocalFileMac.h"
68 // for chflags()
69 #  include <sys/stat.h>
70 #  include <unistd.h>
71 #endif
72 #ifdef XP_UNIX
73 #  include <ctype.h>
74 #endif
75 #ifdef XP_IOS
76 #  include "UIKitDirProvider.h"
77 #endif
78 
79 #if defined(MOZ_SANDBOX)
80 #  include "mozilla/SandboxSettings.h"
81 #  include "nsID.h"
82 #  include "mozilla/Unused.h"
83 #  if defined(XP_WIN)
84 #    include "sandboxBroker.h"
85 #  endif
86 #endif
87 
88 #if defined(XP_MACOSX)
89 #  define APP_REGISTRY_NAME "Application Registry"
90 #elif defined(XP_WIN)
91 #  define APP_REGISTRY_NAME "registry.dat"
92 #else
93 #  define APP_REGISTRY_NAME "appreg"
94 #endif
95 
96 #define PREF_OVERRIDE_DIRNAME "preferences"
97 
98 #if defined(MOZ_SANDBOX)
99 static already_AddRefed<nsIFile> GetProcessSandboxTempDir(
100     GeckoProcessType type);
101 static nsresult DeleteDirIfExists(nsIFile* dir);
102 static bool IsContentSandboxDisabled();
103 static const char* GetProcessTempBaseDirKey();
104 static already_AddRefed<nsIFile> CreateProcessSandboxTempDir(
105     GeckoProcessType procType);
106 #endif
107 
108 nsXREDirProvider* gDirServiceProvider = nullptr;
109 nsIFile* gDataDirHomeLocal = nullptr;
110 nsIFile* gDataDirHome = nullptr;
111 nsCOMPtr<nsIFile> gDataDirProfileLocal = nullptr;
112 nsCOMPtr<nsIFile> gDataDirProfile = nullptr;
113 
114 // These are required to allow nsXREDirProvider to be usable in xpcshell tests.
115 // where gAppData is null.
116 #if defined(XP_MACOSX) || defined(XP_UNIX)
GetAppName()117 static const char* GetAppName() {
118   if (gAppData) {
119     return gAppData->name;
120   }
121   return nullptr;
122 }
123 #endif
124 
125 #ifdef XP_MACOSX
GetAppVendor()126 static const char* GetAppVendor() {
127   if (gAppData) {
128     return gAppData->vendor;
129   }
130   return nullptr;
131 }
132 #endif
133 
nsXREDirProvider()134 nsXREDirProvider::nsXREDirProvider() : mProfileNotified(false) {
135   gDirServiceProvider = this;
136 }
137 
~nsXREDirProvider()138 nsXREDirProvider::~nsXREDirProvider() {
139   gDirServiceProvider = nullptr;
140   gDataDirHomeLocal = nullptr;
141   gDataDirHome = nullptr;
142 }
143 
GetSingleton()144 already_AddRefed<nsXREDirProvider> nsXREDirProvider::GetSingleton() {
145   if (!gDirServiceProvider) {
146     new nsXREDirProvider();  // This sets gDirServiceProvider
147   }
148   return do_AddRef(gDirServiceProvider);
149 }
150 
Initialize(nsIFile * aXULAppDir,nsIFile * aGREDir,nsIDirectoryServiceProvider * aAppProvider)151 nsresult nsXREDirProvider::Initialize(
152     nsIFile* aXULAppDir, nsIFile* aGREDir,
153     nsIDirectoryServiceProvider* aAppProvider) {
154   NS_ENSURE_ARG(aXULAppDir);
155   NS_ENSURE_ARG(aGREDir);
156 
157   mAppProvider = aAppProvider;
158   mXULAppDir = aXULAppDir;
159   mGREDir = aGREDir;
160   nsCOMPtr<nsIFile> binaryPath;
161   nsresult rv = XRE_GetBinaryPath(getter_AddRefs(binaryPath));
162   if (NS_FAILED(rv)) return rv;
163   rv = binaryPath->GetParent(getter_AddRefs(mGREBinDir));
164   if (NS_FAILED(rv)) return rv;
165 
166   if (!mProfileDir) {
167     nsCOMPtr<nsIDirectoryServiceProvider> app(mAppProvider);
168     if (app) {
169       bool per = false;
170       app->GetFile(NS_APP_USER_PROFILE_50_DIR, &per,
171                    getter_AddRefs(mProfileDir));
172       NS_ASSERTION(per, "NS_APP_USER_PROFILE_50_DIR must be persistent!");
173       NS_ASSERTION(
174           mProfileDir,
175           "NS_APP_USER_PROFILE_50_DIR not defined! This shouldn't happen!");
176     }
177   }
178 
179   return NS_OK;
180 }
181 
SetProfile(nsIFile * aDir,nsIFile * aLocalDir)182 nsresult nsXREDirProvider::SetProfile(nsIFile* aDir, nsIFile* aLocalDir) {
183   NS_ASSERTION(aDir && aLocalDir, "We don't support no-profile apps yet!");
184 
185   nsresult rv;
186 
187   rv = EnsureDirectoryExists(aDir);
188   if (NS_FAILED(rv)) return rv;
189 
190   rv = EnsureDirectoryExists(aLocalDir);
191   if (NS_FAILED(rv)) return rv;
192 
193 #ifdef XP_MACOSX
194   bool same;
195   if (NS_SUCCEEDED(aDir->Equals(aLocalDir, &same)) && !same) {
196     // Ensure that the cache directory is not indexed by Spotlight
197     // (bug 718910).  At least on OS X, the cache directory (under
198     // ~/Library/Caches/) is always the "local" user profile
199     // directory.  This is confusing, since *both* user profile
200     // directories are "local" (they both exist under the user's
201     // home directory).  But this usage dates back at least as far
202     // as the patch for bug 291033, where "local" seems to mean
203     // "suitable for temporary storage".  Don't hide the cache
204     // directory if by some chance it and the "non-local" profile
205     // directory are the same -- there are bad side effects from
206     // hiding a profile directory under /Library/Application Support/
207     // (see bug 801883).
208     nsAutoCString cacheDir;
209     if (NS_SUCCEEDED(aLocalDir->GetNativePath(cacheDir))) {
210       if (chflags(cacheDir.get(), UF_HIDDEN)) {
211         NS_WARNING("Failed to set Cache directory to HIDDEN.");
212       }
213     }
214   }
215 #endif
216 
217   mProfileDir = aDir;
218   mProfileLocalDir = aLocalDir;
219   return NS_OK;
220 }
221 
NS_IMPL_QUERY_INTERFACE(nsXREDirProvider,nsIDirectoryServiceProvider,nsIDirectoryServiceProvider2,nsIXREDirProvider,nsIProfileStartup)222 NS_IMPL_QUERY_INTERFACE(nsXREDirProvider, nsIDirectoryServiceProvider,
223                         nsIDirectoryServiceProvider2, nsIXREDirProvider,
224                         nsIProfileStartup)
225 
226 NS_IMETHODIMP_(MozExternalRefCountType)
227 nsXREDirProvider::AddRef() { return 1; }
228 
NS_IMETHODIMP_(MozExternalRefCountType)229 NS_IMETHODIMP_(MozExternalRefCountType)
230 nsXREDirProvider::Release() { return 0; }
231 
GetUserProfilesRootDir(nsIFile ** aResult)232 nsresult nsXREDirProvider::GetUserProfilesRootDir(nsIFile** aResult) {
233   nsCOMPtr<nsIFile> file;
234   nsresult rv = GetUserDataDirectory(getter_AddRefs(file), false);
235 
236   if (NS_SUCCEEDED(rv)) {
237 #if !defined(XP_UNIX) || defined(XP_MACOSX)
238     rv = file->AppendNative("Profiles"_ns);
239 #endif
240     // We must create the profile directory here if it does not exist.
241     nsresult tmp = EnsureDirectoryExists(file);
242     if (NS_FAILED(tmp)) {
243       rv = tmp;
244     }
245   }
246   file.swap(*aResult);
247   return rv;
248 }
249 
GetUserProfilesLocalDir(nsIFile ** aResult)250 nsresult nsXREDirProvider::GetUserProfilesLocalDir(nsIFile** aResult) {
251   nsCOMPtr<nsIFile> file;
252   nsresult rv = GetUserDataDirectory(getter_AddRefs(file), true);
253 
254   if (NS_SUCCEEDED(rv)) {
255 #if !defined(XP_UNIX) || defined(XP_MACOSX)
256     rv = file->AppendNative("Profiles"_ns);
257 #endif
258     // We must create the profile directory here if it does not exist.
259     nsresult tmp = EnsureDirectoryExists(file);
260     if (NS_FAILED(tmp)) {
261       rv = tmp;
262     }
263   }
264   file.swap(*aResult);
265   return NS_OK;
266 }
267 
268 #if defined(XP_UNIX) || defined(XP_MACOSX)
269 /**
270  * Get the directory that is the parent of the system-wide directories
271  * for extensions and native manifests.
272  *
273  * On OSX this is /Library/Application Support/Mozilla
274  * On Linux this is /usr/{lib,lib64}/mozilla
275  *   (for 32- and 64-bit systems respsectively)
276  */
GetSystemParentDirectory(nsIFile ** aFile)277 static nsresult GetSystemParentDirectory(nsIFile** aFile) {
278   nsresult rv;
279   nsCOMPtr<nsIFile> localDir;
280 #  if defined(XP_MACOSX)
281   rv = GetOSXFolderType(kOnSystemDisk, kApplicationSupportFolderType,
282                         getter_AddRefs(localDir));
283   if (NS_SUCCEEDED(rv)) {
284     rv = localDir->AppendNative("Mozilla"_ns);
285   }
286 #  else
287   constexpr auto dirname =
288 #    ifdef HAVE_USR_LIB64_DIR
289       "/usr/lib64/mozilla"_ns
290 #    elif defined(__OpenBSD__) || defined(__FreeBSD__)
291       "/usr/local/lib/mozilla"_ns
292 #    else
293       "/usr/lib/mozilla"_ns
294 #    endif
295       ;
296   rv = NS_NewNativeLocalFile(dirname, false, getter_AddRefs(localDir));
297 #  endif
298 
299   if (NS_SUCCEEDED(rv)) {
300     localDir.forget(aFile);
301   }
302   return rv;
303 }
304 #endif
305 
306 NS_IMETHODIMP
GetFile(const char * aProperty,bool * aPersistent,nsIFile ** aFile)307 nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
308                           nsIFile** aFile) {
309   nsresult rv;
310 
311   bool gettingProfile = false;
312 
313   if (!strcmp(aProperty, NS_APP_USER_PROFILE_LOCAL_50_DIR)) {
314     // If XRE_NotifyProfile hasn't been called, don't fall through to
315     // mAppProvider on the profile keys.
316     if (!mProfileNotified) return NS_ERROR_FAILURE;
317 
318     if (mProfileLocalDir) return mProfileLocalDir->Clone(aFile);
319 
320     if (mAppProvider)
321       return mAppProvider->GetFile(aProperty, aPersistent, aFile);
322 
323     // This falls through to the case below
324     gettingProfile = true;
325   }
326   if (!strcmp(aProperty, NS_APP_USER_PROFILE_50_DIR) || gettingProfile) {
327     if (!mProfileNotified) return NS_ERROR_FAILURE;
328 
329     if (mProfileDir) return mProfileDir->Clone(aFile);
330 
331     if (mAppProvider)
332       return mAppProvider->GetFile(aProperty, aPersistent, aFile);
333 
334     // If we don't succeed here, bail early so that we aren't reentrant
335     // through the "GetProfileDir" call below.
336     return NS_ERROR_FAILURE;
337   }
338 
339   if (mAppProvider) {
340     rv = mAppProvider->GetFile(aProperty, aPersistent, aFile);
341     if (NS_SUCCEEDED(rv) && *aFile) return rv;
342   }
343 
344   *aPersistent = true;
345 
346   if (!strcmp(aProperty, NS_GRE_DIR)) {
347 #if defined(MOZ_WIDGET_ANDROID)
348     // On Android, internal files are inside the APK, a zip file, so this
349     // folder doesn't really make sense.
350     return NS_ERROR_FAILURE;
351 #else
352     return mGREDir->Clone(aFile);
353 #endif
354   } else if (!strcmp(aProperty, NS_GRE_BIN_DIR)) {
355     return mGREBinDir->Clone(aFile);
356   } else if (!strcmp(aProperty, NS_OS_CURRENT_PROCESS_DIR) ||
357              !strcmp(aProperty, NS_APP_INSTALL_CLEANUP_DIR)) {
358     return GetAppDir()->Clone(aFile);
359   }
360 
361   rv = NS_ERROR_FAILURE;
362   nsCOMPtr<nsIFile> file;
363 
364   if (!strcmp(aProperty, NS_APP_PREF_DEFAULTS_50_DIR)) {
365 #if defined(MOZ_WIDGET_ANDROID)
366     // Same as NS_GRE_DIR
367     return NS_ERROR_FAILURE;
368 #else
369     // return the GRE default prefs directory here, and the app default prefs
370     // directory (if applicable) in NS_APP_PREFS_DEFAULTS_DIR_LIST.
371     rv = mGREDir->Clone(getter_AddRefs(file));
372     if (NS_SUCCEEDED(rv)) {
373       rv = file->AppendNative("defaults"_ns);
374       if (NS_SUCCEEDED(rv)) rv = file->AppendNative("pref"_ns);
375     }
376 #endif
377   } else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_DIR) ||
378              !strcmp(aProperty, XRE_USER_APP_DATA_DIR)) {
379     rv = GetUserAppDataDirectory(getter_AddRefs(file));
380   }
381 #if defined(XP_UNIX) || defined(XP_MACOSX)
382   else if (!strcmp(aProperty, XRE_SYS_NATIVE_MANIFESTS)) {
383     nsCOMPtr<nsIFile> localDir;
384 
385     rv = ::GetSystemParentDirectory(getter_AddRefs(localDir));
386     if (NS_SUCCEEDED(rv)) {
387       localDir.swap(file);
388     }
389   } else if (!strcmp(aProperty, XRE_USER_NATIVE_MANIFESTS)) {
390     nsCOMPtr<nsIFile> localDir;
391     rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false);
392     if (NS_SUCCEEDED(rv)) {
393 #  if defined(XP_MACOSX)
394       rv = localDir->AppendNative("Mozilla"_ns);
395 #  else
396       rv = localDir->AppendNative(".mozilla"_ns);
397 #  endif
398     }
399     if (NS_SUCCEEDED(rv)) {
400       localDir.swap(file);
401     }
402   }
403 #endif
404   else if (!strcmp(aProperty, XRE_UPDATE_ROOT_DIR)) {
405     rv = GetUpdateRootDir(getter_AddRefs(file));
406   } else if (!strcmp(aProperty, XRE_OLD_UPDATE_ROOT_DIR)) {
407     rv = GetUpdateRootDir(getter_AddRefs(file), true);
408   } else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_FILE)) {
409     rv = GetUserAppDataDirectory(getter_AddRefs(file));
410     if (NS_SUCCEEDED(rv))
411       rv = file->AppendNative(nsLiteralCString(APP_REGISTRY_NAME));
412   } else if (!strcmp(aProperty, NS_APP_USER_PROFILES_ROOT_DIR)) {
413     rv = GetUserProfilesRootDir(getter_AddRefs(file));
414   } else if (!strcmp(aProperty, NS_APP_USER_PROFILES_LOCAL_ROOT_DIR)) {
415     rv = GetUserProfilesLocalDir(getter_AddRefs(file));
416   } else if (!strcmp(aProperty, XRE_EXECUTABLE_FILE)) {
417     nsCOMPtr<nsIFile> lf;
418     rv = XRE_GetBinaryPath(getter_AddRefs(lf));
419     if (NS_SUCCEEDED(rv)) file = lf;
420   }
421 
422   else if (!strcmp(aProperty, NS_APP_PROFILE_DIR_STARTUP) && mProfileDir) {
423     return mProfileDir->Clone(aFile);
424   } else if (!strcmp(aProperty, NS_APP_PROFILE_LOCAL_DIR_STARTUP)) {
425     if (mProfileLocalDir) return mProfileLocalDir->Clone(aFile);
426 
427     if (mProfileDir) return mProfileDir->Clone(aFile);
428 
429     if (mAppProvider)
430       return mAppProvider->GetFile(NS_APP_PROFILE_DIR_STARTUP, aPersistent,
431                                    aFile);
432   }
433 #if defined(XP_UNIX) || defined(XP_MACOSX)
434   else if (!strcmp(aProperty, XRE_SYS_LOCAL_EXTENSION_PARENT_DIR)) {
435 #  ifdef ENABLE_SYSTEM_EXTENSION_DIRS
436     return GetSystemExtensionsDirectory(aFile);
437 #  else
438     return NS_ERROR_FAILURE;
439 #  endif
440   }
441 #endif
442 #if defined(XP_UNIX) && !defined(XP_MACOSX)
443   else if (!strcmp(aProperty, XRE_SYS_SHARE_EXTENSION_PARENT_DIR)) {
444 #  ifdef ENABLE_SYSTEM_EXTENSION_DIRS
445 #    if defined(__OpenBSD__) || defined(__FreeBSD__)
446     static const char* const sysLExtDir = "/usr/local/lib/xpi";
447 #    else
448     static const char* const sysLExtDir = "/usr/lib/xpi";
449 #    endif
450     return NS_NewNativeLocalFile(nsDependentCString(sysLExtDir), false, aFile);
451 #  else
452     return NS_ERROR_FAILURE;
453 #  endif
454   }
455 #endif
456   else if (!strcmp(aProperty, XRE_USER_SYS_EXTENSION_DIR)) {
457 #ifdef ENABLE_SYSTEM_EXTENSION_DIRS
458     return GetSysUserExtensionsDirectory(aFile);
459 #else
460     return NS_ERROR_FAILURE;
461 #endif
462   } else if (!strcmp(aProperty, XRE_USER_RUNTIME_DIR)) {
463 #if defined(XP_UNIX)
464     nsPrintfCString path("/run/user/%d/%s/", getuid(), GetAppName());
465     ToLowerCase(path);
466     return NS_NewNativeLocalFile(path, false, aFile);
467 #else
468     return NS_ERROR_FAILURE;
469 #endif
470   } else if (!strcmp(aProperty, XRE_APP_DISTRIBUTION_DIR)) {
471     bool persistent = false;
472     rv = GetFile(NS_GRE_DIR, &persistent, getter_AddRefs(file));
473     if (NS_SUCCEEDED(rv)) rv = file->AppendNative("distribution"_ns);
474   } else if (!strcmp(aProperty, XRE_APP_FEATURES_DIR)) {
475     rv = GetAppDir()->Clone(getter_AddRefs(file));
476     if (NS_SUCCEEDED(rv)) rv = file->AppendNative("features"_ns);
477   } else if (!strcmp(aProperty, XRE_ADDON_APP_DIR)) {
478     nsCOMPtr<nsIDirectoryServiceProvider> dirsvc(
479         do_GetService("@mozilla.org/file/directory_service;1", &rv));
480     if (NS_FAILED(rv)) return rv;
481     bool unused;
482     rv = dirsvc->GetFile("XCurProcD", &unused, getter_AddRefs(file));
483   }
484 #if defined(MOZ_SANDBOX)
485   else if (!strcmp(aProperty, NS_APP_CONTENT_PROCESS_TEMP_DIR)) {
486     if (!mContentTempDir && NS_FAILED((rv = LoadContentProcessTempDir()))) {
487       return rv;
488     }
489     rv = mContentTempDir->Clone(getter_AddRefs(file));
490   }
491 #endif  // defined(MOZ_SANDBOX)
492   else if (NS_SUCCEEDED(GetProfileStartupDir(getter_AddRefs(file)))) {
493     // We need to allow component, xpt, and chrome registration to
494     // occur prior to the profile-after-change notification.
495     if (!strcmp(aProperty, NS_APP_USER_CHROME_DIR)) {
496       rv = file->AppendNative("chrome"_ns);
497     }
498   }
499 
500   if (NS_SUCCEEDED(rv) && file) {
501     file.forget(aFile);
502     return NS_OK;
503   }
504 
505   bool ensureFilePermissions = false;
506 
507   if (NS_SUCCEEDED(GetProfileDir(getter_AddRefs(file)))) {
508     if (!strcmp(aProperty, NS_APP_PREFS_50_DIR)) {
509       rv = NS_OK;
510     } else if (!strcmp(aProperty, NS_APP_PREFS_50_FILE)) {
511       rv = file->AppendNative("prefs.js"_ns);
512     } else if (!strcmp(aProperty, NS_APP_PREFS_OVERRIDE_DIR)) {
513       rv = mProfileDir->Clone(getter_AddRefs(file));
514       nsresult tmp =
515           file->AppendNative(nsLiteralCString(PREF_OVERRIDE_DIRNAME));
516       if (NS_FAILED(tmp)) {
517         rv = tmp;
518       }
519       tmp = EnsureDirectoryExists(file);
520       if (NS_FAILED(tmp)) {
521         rv = tmp;
522       }
523     }
524   }
525   if (NS_FAILED(rv) || !file) return NS_ERROR_FAILURE;
526 
527   if (ensureFilePermissions) {
528     bool fileToEnsureExists;
529     bool isWritable;
530     if (NS_SUCCEEDED(file->Exists(&fileToEnsureExists)) && fileToEnsureExists &&
531         NS_SUCCEEDED(file->IsWritable(&isWritable)) && !isWritable) {
532       uint32_t permissions;
533       if (NS_SUCCEEDED(file->GetPermissions(&permissions))) {
534         rv = file->SetPermissions(permissions | 0600);
535         NS_ASSERTION(NS_SUCCEEDED(rv), "failed to ensure file permissions");
536       }
537     }
538   }
539 
540   file.forget(aFile);
541   return NS_OK;
542 }
543 
LoadDirIntoArray(nsIFile * dir,const char * const * aAppendList,nsCOMArray<nsIFile> & aDirectories)544 static void LoadDirIntoArray(nsIFile* dir, const char* const* aAppendList,
545                              nsCOMArray<nsIFile>& aDirectories) {
546   if (!dir) return;
547 
548   nsCOMPtr<nsIFile> subdir;
549   dir->Clone(getter_AddRefs(subdir));
550   if (!subdir) return;
551 
552   for (const char* const* a = aAppendList; *a; ++a) {
553     subdir->AppendNative(nsDependentCString(*a));
554   }
555 
556   bool exists;
557   if (NS_SUCCEEDED(subdir->Exists(&exists)) && exists) {
558     aDirectories.AppendObject(subdir);
559   }
560 }
561 
562 NS_IMETHODIMP
GetFiles(const char * aProperty,nsISimpleEnumerator ** aResult)563 nsXREDirProvider::GetFiles(const char* aProperty,
564                            nsISimpleEnumerator** aResult) {
565   nsresult rv;
566 
567   nsCOMPtr<nsISimpleEnumerator> appEnum;
568   nsCOMPtr<nsIDirectoryServiceProvider2> appP2(do_QueryInterface(mAppProvider));
569   if (appP2) {
570     rv = appP2->GetFiles(aProperty, getter_AddRefs(appEnum));
571     if (NS_FAILED(rv)) {
572       appEnum = nullptr;
573     } else if (rv != NS_SUCCESS_AGGREGATE_RESULT) {
574       appEnum.forget(aResult);
575       return NS_OK;
576     }
577   }
578 
579   nsCOMPtr<nsISimpleEnumerator> xreEnum;
580   rv = GetFilesInternal(aProperty, getter_AddRefs(xreEnum));
581   if (NS_FAILED(rv)) {
582     if (appEnum) {
583       appEnum.forget(aResult);
584       return NS_SUCCESS_AGGREGATE_RESULT;
585     }
586 
587     return rv;
588   }
589 
590   rv = NS_NewUnionEnumerator(aResult, appEnum, xreEnum);
591   if (NS_FAILED(rv)) return rv;
592 
593   return NS_SUCCESS_AGGREGATE_RESULT;
594 }
595 
596 #if defined(MOZ_SANDBOX)
597 
GetProcessTempBaseDirKey()598 static const char* GetProcessTempBaseDirKey() {
599 #  if defined(XP_WIN)
600   return NS_WIN_LOW_INTEGRITY_TEMP_BASE;
601 #  else
602   return NS_OS_TEMP_DIR;
603 #  endif
604 }
605 
606 //
607 // Sets mContentTempDir so that it refers to the appropriate temp dir.
608 // If the sandbox is enabled, NS_APP_CONTENT_PROCESS_TEMP_DIR, otherwise
609 // NS_OS_TEMP_DIR is used.
610 //
LoadContentProcessTempDir()611 nsresult nsXREDirProvider::LoadContentProcessTempDir() {
612   // The parent is responsible for creating the sandbox temp dir.
613   if (XRE_IsParentProcess()) {
614     mContentProcessSandboxTempDir =
615         CreateProcessSandboxTempDir(GeckoProcessType_Content);
616     mContentTempDir = mContentProcessSandboxTempDir;
617   } else {
618     mContentTempDir = !IsContentSandboxDisabled()
619                           ? GetProcessSandboxTempDir(GeckoProcessType_Content)
620                           : nullptr;
621   }
622 
623   if (!mContentTempDir) {
624     nsresult rv =
625         NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mContentTempDir));
626     if (NS_WARN_IF(NS_FAILED(rv))) {
627       return rv;
628     }
629   }
630 
631   return NS_OK;
632 }
633 
IsContentSandboxDisabled()634 static bool IsContentSandboxDisabled() {
635   return !mozilla::BrowserTabsRemoteAutostart() ||
636          (!mozilla::IsContentSandboxEnabled());
637 }
638 
639 //
640 // If a process sandbox temp dir is to be used, returns an nsIFile
641 // for the directory. Returns null if an error occurs.
642 //
GetProcessSandboxTempDir(GeckoProcessType type)643 static already_AddRefed<nsIFile> GetProcessSandboxTempDir(
644     GeckoProcessType type) {
645   nsCOMPtr<nsIFile> localFile;
646 
647   nsresult rv = NS_GetSpecialDirectory(GetProcessTempBaseDirKey(),
648                                        getter_AddRefs(localFile));
649   if (NS_WARN_IF(NS_FAILED(rv))) {
650     return nullptr;
651   }
652 
653   MOZ_ASSERT(type == GeckoProcessType_Content);
654 
655   const char* prefKey = "security.sandbox.content.tempDirSuffix";
656   nsAutoString tempDirSuffix;
657   rv = mozilla::Preferences::GetString(prefKey, tempDirSuffix);
658   if (NS_WARN_IF(NS_FAILED(rv)) || tempDirSuffix.IsEmpty()) {
659     return nullptr;
660   }
661 
662   rv = localFile->Append(u"Temp-"_ns + tempDirSuffix);
663   if (NS_WARN_IF(NS_FAILED(rv))) {
664     return nullptr;
665   }
666 
667   return localFile.forget();
668 }
669 
670 //
671 // Create a temporary directory for use from sandboxed processes.
672 // Only called in the parent. The path is derived from a UUID stored in a
673 // pref which is available to content processes. Returns null
674 // if the content sandbox is disabled or if an error occurs.
675 //
CreateProcessSandboxTempDir(GeckoProcessType procType)676 static already_AddRefed<nsIFile> CreateProcessSandboxTempDir(
677     GeckoProcessType procType) {
678   if ((procType == GeckoProcessType_Content) && IsContentSandboxDisabled()) {
679     return nullptr;
680   }
681 
682   MOZ_ASSERT(procType == GeckoProcessType_Content);
683 
684   // Get (and create if blank) temp directory suffix pref.
685   const char* pref = "security.sandbox.content.tempDirSuffix";
686 
687   nsresult rv;
688   nsAutoString tempDirSuffix;
689   mozilla::Preferences::GetString(pref, tempDirSuffix);
690 
691   if (tempDirSuffix.IsEmpty()) {
692     nsID uuid;
693     rv = nsID::GenerateUUIDInPlace(uuid);
694     if (NS_WARN_IF(NS_FAILED(rv))) {
695       return nullptr;
696     }
697 
698     char uuidChars[NSID_LENGTH];
699     uuid.ToProvidedString(uuidChars);
700     tempDirSuffix.AssignASCII(uuidChars, NSID_LENGTH);
701 #  ifdef XP_UNIX
702     // Braces in a path are somewhat annoying to deal with
703     // and pretty alien on Unix
704     tempDirSuffix.StripChars(u"{}");
705 #  endif
706 
707     // Save the pref
708     rv = mozilla::Preferences::SetString(pref, tempDirSuffix);
709     if (NS_WARN_IF(NS_FAILED(rv))) {
710       // If we fail to save the pref we don't want to create the temp dir,
711       // because we won't be able to clean it up later.
712       return nullptr;
713     }
714 
715     nsCOMPtr<nsIPrefService> prefsvc = mozilla::Preferences::GetService();
716     if (!prefsvc || NS_FAILED((rv = prefsvc->SavePrefFile(nullptr)))) {
717       // Again, if we fail to save the pref file we might not be able to clean
718       // up the temp directory, so don't create one.  Note that in the case
719       // the preference values allows an off main thread save, the successful
720       // return from the call doesn't mean we actually saved the file.  See
721       // bug 1364496 for details.
722       NS_WARNING("Failed to save pref file, cannot create temp dir.");
723       return nullptr;
724     }
725   }
726 
727   nsCOMPtr<nsIFile> sandboxTempDir = GetProcessSandboxTempDir(procType);
728   if (!sandboxTempDir) {
729     NS_WARNING("Failed to determine sandbox temp dir path.");
730     return nullptr;
731   }
732 
733   // Remove the directory. It may exist due to a previous crash.
734   if (NS_FAILED(DeleteDirIfExists(sandboxTempDir))) {
735     NS_WARNING("Failed to reset sandbox temp dir.");
736     return nullptr;
737   }
738 
739   // Create the directory
740   rv = sandboxTempDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
741   if (NS_FAILED(rv)) {
742     NS_WARNING("Failed to create sandbox temp dir.");
743     return nullptr;
744   }
745 
746   return sandboxTempDir.forget();
747 }
748 
DeleteDirIfExists(nsIFile * dir)749 static nsresult DeleteDirIfExists(nsIFile* dir) {
750   if (dir) {
751     // Don't return an error if the directory doesn't exist.
752     // Windows Remove() returns NS_ERROR_FILE_NOT_FOUND while
753     // OS X returns NS_ERROR_FILE_TARGET_DOES_NOT_EXIST.
754     nsresult rv = dir->Remove(/* aRecursive */ true);
755     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND &&
756         rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
757       return rv;
758     }
759   }
760   return NS_OK;
761 }
762 
763 #endif  // defined(MOZ_SANDBOX)
764 
765 static const char* const kAppendPrefDir[] = {"defaults", "preferences",
766                                              nullptr};
767 #ifdef MOZ_BACKGROUNDTASKS
768 static const char* const kAppendBackgroundTasksPrefDir[] = {
769     "defaults", "backgroundtasks", nullptr};
770 #endif
771 
GetFilesInternal(const char * aProperty,nsISimpleEnumerator ** aResult)772 nsresult nsXREDirProvider::GetFilesInternal(const char* aProperty,
773                                             nsISimpleEnumerator** aResult) {
774   nsresult rv = NS_OK;
775   *aResult = nullptr;
776 
777   if (!strcmp(aProperty, NS_APP_PREFS_DEFAULTS_DIR_LIST)) {
778     nsCOMArray<nsIFile> directories;
779 
780     LoadDirIntoArray(mXULAppDir, kAppendPrefDir, directories);
781 #ifdef MOZ_BACKGROUNDTASKS
782     if (mozilla::BackgroundTasks::IsBackgroundTaskMode()) {
783       LoadDirIntoArray(mGREDir, kAppendBackgroundTasksPrefDir, directories);
784       LoadDirIntoArray(mXULAppDir, kAppendBackgroundTasksPrefDir, directories);
785     }
786 #endif
787 
788     rv = NS_NewArrayEnumerator(aResult, directories, NS_GET_IID(nsIFile));
789   } else if (!strcmp(aProperty, NS_APP_CHROME_DIR_LIST)) {
790     // NS_APP_CHROME_DIR_LIST is only used to get default (native) icons
791     // for OS window decoration.
792 
793     static const char* const kAppendChromeDir[] = {"chrome", nullptr};
794     nsCOMArray<nsIFile> directories;
795     LoadDirIntoArray(mXULAppDir, kAppendChromeDir, directories);
796 
797     rv = NS_NewArrayEnumerator(aResult, directories, NS_GET_IID(nsIFile));
798   } else
799     rv = NS_ERROR_FAILURE;
800 
801   return rv;
802 }
803 
804 NS_IMETHODIMP
GetDirectory(nsIFile ** aResult)805 nsXREDirProvider::GetDirectory(nsIFile** aResult) {
806   NS_ENSURE_TRUE(mProfileDir, NS_ERROR_NOT_INITIALIZED);
807 
808   return mProfileDir->Clone(aResult);
809 }
810 
InitializeUserPrefs()811 void nsXREDirProvider::InitializeUserPrefs() {
812   if (!mPrefsInitialized) {
813     // Temporarily set mProfileNotified to true so that the preference service
814     // can access the profile directory during initialization. Afterwards, clear
815     // it so that no other code can inadvertently access it until we get to
816     // profile-do-change.
817     mozilla::AutoRestore<bool> ar(mProfileNotified);
818     mProfileNotified = true;
819 
820     mozilla::Preferences::InitializeUserPrefs();
821   }
822 }
823 
FinishInitializingUserPrefs()824 void nsXREDirProvider::FinishInitializingUserPrefs() {
825   if (!mPrefsInitialized) {
826     // See InitializeUserPrefs above.
827     mozilla::AutoRestore<bool> ar(mProfileNotified);
828     mProfileNotified = true;
829 
830     mozilla::Preferences::FinishInitializingUserPrefs();
831 
832     mPrefsInitialized = true;
833   }
834 }
835 
836 NS_IMETHODIMP
DoStartup()837 nsXREDirProvider::DoStartup() {
838   nsresult rv;
839 
840   if (!mProfileNotified) {
841     nsCOMPtr<nsIObserverService> obsSvc =
842         mozilla::services::GetObserverService();
843     if (!obsSvc) return NS_ERROR_FAILURE;
844 
845     mProfileNotified = true;
846 
847     /*
848        Make sure we've setup prefs before profile-do-change to be able to use
849        them to track crashes and because we want to begin crash tracking before
850        other code run from this notification since they may cause crashes.
851     */
852     MOZ_ASSERT(mPrefsInitialized);
853 
854     bool safeModeNecessary = false;
855     nsCOMPtr<nsIAppStartup> appStartup(
856         mozilla::components::AppStartup::Service());
857     if (appStartup) {
858       rv = appStartup->TrackStartupCrashBegin(&safeModeNecessary);
859       if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)
860         NS_WARNING("Error while beginning startup crash tracking");
861 
862       if (!gSafeMode && safeModeNecessary) {
863         appStartup->RestartInSafeMode(nsIAppStartup::eForceQuit);
864         return NS_OK;
865       }
866     }
867 
868     static const char16_t kStartup[] = {'s', 't', 'a', 'r',
869                                         't', 'u', 'p', '\0'};
870     obsSvc->NotifyObservers(nullptr, "profile-do-change", kStartup);
871 
872     // Initialize the Enterprise Policies service in the parent process
873     // In the content process it's loaded on demand when needed
874     if (XRE_IsParentProcess()) {
875       nsCOMPtr<nsIObserver> policies(
876           do_GetService("@mozilla.org/enterprisepolicies;1"));
877       if (policies) {
878         policies->Observe(nullptr, "policies-startup", nullptr);
879       }
880     }
881 
882 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
883     // Call SandboxBroker to initialize things that depend on Gecko machinery
884     // like the directory provider. We insert this initialization code here
885     // (rather than in XRE_mainRun) because we need NS_APP_USER_PROFILE_50_DIR
886     // to be known and so that any child content processes spawned by extensions
887     // from the notifications below will have all the requisite directories
888     // white-listed for read/write access. An example of this is the
889     // tor-launcher launching the network configuration window. See bug 1485836.
890     mozilla::SandboxBroker::GeckoDependentInitialize();
891 #endif
892 
893 #ifdef MOZ_THUNDERBIRD
894     bool bgtaskMode = false;
895 #  ifdef MOZ_BACKGROUNDTASKS
896     bgtaskMode = mozilla::BackgroundTasks::IsBackgroundTaskMode();
897 #  endif
898     if (!bgtaskMode &&
899         mozilla::Preferences::GetBool(
900             "security.prompt_for_master_password_on_startup", false)) {
901       // Prompt for the master password prior to opening application windows,
902       // to avoid the race that triggers multiple prompts (see bug 177175).
903       // We use this code until we have a better solution, possibly as
904       // described in bug 177175 comment 384.
905       nsCOMPtr<nsIPK11TokenDB> db =
906           do_GetService("@mozilla.org/security/pk11tokendb;1");
907       if (db) {
908         nsCOMPtr<nsIPK11Token> token;
909         if (NS_SUCCEEDED(db->GetInternalKeyToken(getter_AddRefs(token)))) {
910           mozilla::Unused << token->Login(false);
911         }
912       } else {
913         NS_WARNING("Failed to get nsIPK11TokenDB service.");
914       }
915     }
916 #endif
917 
918     bool initExtensionManager =
919 #ifdef MOZ_BACKGROUNDTASKS
920         !mozilla::BackgroundTasks::IsBackgroundTaskMode();
921 #else
922         true;
923 #endif
924     if (initExtensionManager) {
925       // Init the Extension Manager
926       nsCOMPtr<nsIObserver> em =
927           do_GetService("@mozilla.org/addons/integration;1");
928       if (em) {
929         em->Observe(nullptr, "addons-startup", nullptr);
930       } else {
931         NS_WARNING("Failed to create Addons Manager.");
932       }
933     }
934 
935     obsSvc->NotifyObservers(nullptr, "profile-after-change", kStartup);
936 
937     // Any component that has registered for the profile-after-change category
938     // should also be created at this time.
939     (void)NS_CreateServicesFromCategory("profile-after-change", nullptr,
940                                         "profile-after-change");
941 
942     if (gSafeMode && safeModeNecessary) {
943       static const char16_t kCrashed[] = {'c', 'r', 'a', 's',
944                                           'h', 'e', 'd', '\0'};
945       obsSvc->NotifyObservers(nullptr, "safemode-forced", kCrashed);
946     }
947 
948     // 1 = Regular mode, 2 = Safe mode, 3 = Safe mode forced
949     int mode = 1;
950     if (gSafeMode) {
951       if (safeModeNecessary)
952         mode = 3;
953       else
954         mode = 2;
955     }
956     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SAFE_MODE_USAGE, mode);
957 
958     obsSvc->NotifyObservers(nullptr, "profile-initial-state", nullptr);
959 
960 #if defined(MOZ_SANDBOX)
961     // Makes sure the content temp dir has been loaded if it hasn't been
962     // already. In the parent this ensures it has been created before we attempt
963     // to start any content processes.
964     if (!mContentTempDir) {
965       mozilla::Unused << NS_WARN_IF(NS_FAILED(LoadContentProcessTempDir()));
966     }
967 #endif
968   }
969   return NS_OK;
970 }
971 
DoShutdown()972 void nsXREDirProvider::DoShutdown() {
973   AUTO_PROFILER_LABEL("nsXREDirProvider::DoShutdown", OTHER);
974 
975   if (mProfileNotified) {
976     mozilla::AppShutdown::AdvanceShutdownPhase(
977         mozilla::ShutdownPhase::AppShutdownNetTeardown, nullptr);
978     mozilla::AppShutdown::AdvanceShutdownPhase(
979         mozilla::ShutdownPhase::AppShutdownTeardown, nullptr);
980 
981 #ifdef DEBUG
982     // Not having this causes large intermittent leaks. See bug 1340425.
983     if (JSContext* cx = mozilla::dom::danger::GetJSContext()) {
984       JS_GC(cx);
985     }
986 #endif
987 
988     mozilla::AppShutdown::AdvanceShutdownPhase(
989         mozilla::ShutdownPhase::AppShutdown, nullptr);
990     mozilla::AppShutdown::AdvanceShutdownPhase(
991         mozilla::ShutdownPhase::AppShutdownQM, nullptr);
992     mozilla::AppShutdown::AdvanceShutdownPhase(
993         mozilla::ShutdownPhase::AppShutdownTelemetry, nullptr);
994     mProfileNotified = false;
995   }
996 
997   gDataDirProfileLocal = nullptr;
998   gDataDirProfile = nullptr;
999 
1000   if (XRE_IsParentProcess()) {
1001 #if defined(MOZ_SANDBOX)
1002     mozilla::Unused << DeleteDirIfExists(mContentProcessSandboxTempDir);
1003 #endif
1004   }
1005 }
1006 
1007 #ifdef XP_WIN
GetShellFolderPath(KNOWNFOLDERID folder,nsAString & _retval)1008 static nsresult GetShellFolderPath(KNOWNFOLDERID folder, nsAString& _retval) {
1009   DWORD flags = KF_FLAG_SIMPLE_IDLIST | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_ALIAS;
1010   PWSTR path = nullptr;
1011 
1012   if (!SUCCEEDED(SHGetKnownFolderPath(folder, flags, NULL, &path))) {
1013     return NS_ERROR_NOT_AVAILABLE;
1014   }
1015 
1016   _retval = nsDependentString(path);
1017   CoTaskMemFree(path);
1018   return NS_OK;
1019 }
1020 
1021 /**
1022  * Provides a fallback for getting the path to APPDATA or LOCALAPPDATA by
1023  * querying the registry when the call to SHGetSpecialFolderLocation or
1024  * SHGetPathFromIDListW is unable to provide these paths (Bug 513958).
1025  */
GetRegWindowsAppDataFolder(bool aLocal,nsAString & _retval)1026 static nsresult GetRegWindowsAppDataFolder(bool aLocal, nsAString& _retval) {
1027   HKEY key;
1028   LPCWSTR keyName =
1029       L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
1030   DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, keyName, 0, KEY_READ, &key);
1031   if (res != ERROR_SUCCESS) {
1032     _retval.SetLength(0);
1033     return NS_ERROR_NOT_AVAILABLE;
1034   }
1035 
1036   DWORD type, size;
1037   res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"), nullptr,
1038                          &type, nullptr, &size);
1039   // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
1040   // buffer size must not equal 0, and the buffer size be a multiple of 2.
1041   if (res != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0) {
1042     ::RegCloseKey(key);
1043     _retval.SetLength(0);
1044     return NS_ERROR_NOT_AVAILABLE;
1045   }
1046 
1047   // |size| may or may not include room for the terminating null character
1048   DWORD resultLen = size / 2;
1049 
1050   if (!_retval.SetLength(resultLen, mozilla::fallible)) {
1051     ::RegCloseKey(key);
1052     _retval.SetLength(0);
1053     return NS_ERROR_NOT_AVAILABLE;
1054   }
1055 
1056   auto begin = _retval.BeginWriting();
1057 
1058   res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"), nullptr,
1059                          nullptr, (LPBYTE)begin, &size);
1060   ::RegCloseKey(key);
1061   if (res != ERROR_SUCCESS) {
1062     _retval.SetLength(0);
1063     return NS_ERROR_NOT_AVAILABLE;
1064   }
1065 
1066   if (!_retval.CharAt(resultLen - 1)) {
1067     // It was already null terminated.
1068     _retval.Truncate(resultLen - 1);
1069   }
1070 
1071   return NS_OK;
1072 }
1073 #endif
1074 
HashInstallPath(nsAString & aInstallPath,nsAString & aPathHash)1075 static nsresult HashInstallPath(nsAString& aInstallPath, nsAString& aPathHash) {
1076   mozilla::UniquePtr<NS_tchar[]> hash;
1077   bool success = ::GetInstallHash(PromiseFlatString(aInstallPath).get(), hash);
1078   if (!success) {
1079     return NS_ERROR_FAILURE;
1080   }
1081 
1082   // The hash string is a NS_tchar*, which is wchar* in Windows and char*
1083   // elsewhere.
1084 #ifdef XP_WIN
1085   aPathHash.Assign(hash.get());
1086 #else
1087   aPathHash.AssignASCII(hash.get());
1088 #endif
1089   return NS_OK;
1090 }
1091 
1092 /**
1093  * Gets a hash of the installation directory.
1094  */
GetInstallHash(nsAString & aPathHash)1095 nsresult nsXREDirProvider::GetInstallHash(nsAString& aPathHash) {
1096   nsAutoString stringToHash;
1097 
1098 #ifdef XP_WIN
1099   if (mozilla::widget::WinUtils::HasPackageIdentity()) {
1100     // For packages, the install path includes the version number, so it isn't
1101     // a stable or consistent identifier for the installation. The package
1102     // family name is though, so use that instead of the path.
1103     stringToHash = mozilla::widget::WinUtils::GetPackageFamilyName();
1104   } else
1105 #endif
1106   {
1107     nsCOMPtr<nsIFile> installDir;
1108     nsCOMPtr<nsIFile> appFile;
1109     bool per = false;
1110     nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
1111     NS_ENSURE_SUCCESS(rv, rv);
1112     rv = appFile->GetParent(getter_AddRefs(installDir));
1113     NS_ENSURE_SUCCESS(rv, rv);
1114 
1115     // It is possible that the path we have is on a case insensitive
1116     // filesystem in which case the path may vary depending on how the
1117     // application is called. We want to normalize the case somehow.
1118 #ifdef XP_WIN
1119     // Windows provides a way to get the correct case.
1120     if (!mozilla::widget::WinUtils::ResolveJunctionPointsAndSymLinks(
1121             installDir)) {
1122       NS_WARNING("Failed to resolve install directory.");
1123     }
1124 #elif defined(MOZ_WIDGET_COCOA)
1125     // On OSX roundtripping through an FSRef fixes the case.
1126     FSRef ref;
1127     nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(installDir);
1128     rv = macFile->GetFSRef(&ref);
1129     NS_ENSURE_SUCCESS(rv, rv);
1130     rv = NS_NewLocalFileWithFSRef(&ref, true, getter_AddRefs(macFile));
1131     NS_ENSURE_SUCCESS(rv, rv);
1132     installDir = static_cast<nsIFile*>(macFile);
1133 #endif
1134     // On linux XRE_EXECUTABLE_FILE already seems to be set to the correct path.
1135 
1136     rv = installDir->GetPath(stringToHash);
1137     NS_ENSURE_SUCCESS(rv, rv);
1138   }
1139 
1140   // If we somehow failed to get an actual value, hashing an empty string could
1141   // potentially cause some serious problems given all the things this hash is
1142   // used for. So we don't allow that.
1143   if (stringToHash.IsEmpty()) {
1144     return NS_ERROR_FAILURE;
1145   }
1146 
1147   return HashInstallPath(stringToHash, aPathHash);
1148 }
1149 
1150 /**
1151  * Before bug 1555319 the directory hashed can have had an incorrect case.
1152  * Access to that hash is still available through this function. It is needed so
1153  * we can migrate users who may have an incorrect hash in profiles.ini. This
1154  * support can probably be removed in a few releases time.
1155  */
GetLegacyInstallHash(nsAString & aPathHash)1156 nsresult nsXREDirProvider::GetLegacyInstallHash(nsAString& aPathHash) {
1157   nsCOMPtr<nsIFile> installDir;
1158   nsCOMPtr<nsIFile> appFile;
1159   bool per = false;
1160   nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
1161   NS_ENSURE_SUCCESS(rv, rv);
1162   rv = appFile->GetParent(getter_AddRefs(installDir));
1163   NS_ENSURE_SUCCESS(rv, rv);
1164 
1165   nsAutoString installPath;
1166   rv = installDir->GetPath(installPath);
1167   NS_ENSURE_SUCCESS(rv, rv);
1168 
1169 #ifdef XP_WIN
1170 #  if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
1171   // Convert a 64-bit install path to what would have been the 32-bit install
1172   // path to allow users to migrate their profiles from one to the other.
1173   PWSTR pathX86 = nullptr;
1174   HRESULT hres =
1175       SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, 0, nullptr, &pathX86);
1176   if (SUCCEEDED(hres)) {
1177     nsDependentString strPathX86(pathX86);
1178     if (!StringBeginsWith(installPath, strPathX86,
1179                           nsCaseInsensitiveStringComparator)) {
1180       PWSTR path = nullptr;
1181       hres = SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, nullptr, &path);
1182       if (SUCCEEDED(hres)) {
1183         if (StringBeginsWith(installPath, nsDependentString(path),
1184                              nsCaseInsensitiveStringComparator)) {
1185           installPath.Replace(0, wcslen(path), strPathX86);
1186         }
1187       }
1188       CoTaskMemFree(path);
1189     }
1190   }
1191   CoTaskMemFree(pathX86);
1192 #  endif
1193 #endif
1194   return HashInstallPath(installPath, aPathHash);
1195 }
1196 
GetUpdateRootDir(nsIFile ** aResult,bool aGetOldLocation)1197 nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult,
1198                                             bool aGetOldLocation) {
1199 #ifndef XP_WIN
1200   // There is no old update location on platforms other than Windows. Windows is
1201   // the only platform for which we migrated the update directory.
1202   if (aGetOldLocation) {
1203     return NS_ERROR_NOT_IMPLEMENTED;
1204   }
1205 #endif
1206   nsCOMPtr<nsIFile> updRoot;
1207   nsCOMPtr<nsIFile> appFile;
1208   bool per = false;
1209   nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
1210   NS_ENSURE_SUCCESS(rv, rv);
1211   rv = appFile->GetParent(getter_AddRefs(updRoot));
1212   NS_ENSURE_SUCCESS(rv, rv);
1213 
1214 #ifdef XP_MACOSX
1215   nsCOMPtr<nsIFile> appRootDirFile;
1216   nsCOMPtr<nsIFile> localDir;
1217   nsAutoString appDirPath;
1218   if (NS_FAILED(appFile->GetParent(getter_AddRefs(appRootDirFile))) ||
1219       NS_FAILED(appRootDirFile->GetPath(appDirPath)) ||
1220       NS_FAILED(GetUserDataDirectoryHome(getter_AddRefs(localDir), true))) {
1221     return NS_ERROR_FAILURE;
1222   }
1223 
1224   int32_t dotIndex = appDirPath.RFind(".app");
1225   if (dotIndex == kNotFound) {
1226     dotIndex = appDirPath.Length();
1227   }
1228   appDirPath = Substring(appDirPath, 1, dotIndex - 1);
1229 
1230   bool hasVendor = GetAppVendor() && strlen(GetAppVendor()) != 0;
1231   if (hasVendor || GetAppName()) {
1232     if (NS_FAILED(localDir->AppendNative(
1233             nsDependentCString(hasVendor ? GetAppVendor() : GetAppName())))) {
1234       return NS_ERROR_FAILURE;
1235     }
1236   } else if (NS_FAILED(localDir->AppendNative("Mozilla"_ns))) {
1237     return NS_ERROR_FAILURE;
1238   }
1239 
1240   if (NS_FAILED(localDir->Append(u"updates"_ns)) ||
1241       NS_FAILED(localDir->AppendRelativePath(appDirPath))) {
1242     return NS_ERROR_FAILURE;
1243   }
1244 
1245   localDir.forget(aResult);
1246   return NS_OK;
1247 
1248 #elif XP_WIN
1249   nsAutoString installPath;
1250   rv = updRoot->GetPath(installPath);
1251   NS_ENSURE_SUCCESS(rv, rv);
1252 
1253   mozilla::UniquePtr<wchar_t[]> updatePath;
1254   HRESULT hrv;
1255   if (aGetOldLocation) {
1256     hrv =
1257         GetOldUpdateDirectory(PromiseFlatString(installPath).get(), updatePath);
1258   } else {
1259     hrv = GetCommonUpdateDirectory(PromiseFlatString(installPath).get(),
1260                                    updatePath);
1261   }
1262   if (FAILED(hrv)) {
1263     return NS_ERROR_FAILURE;
1264   }
1265   nsAutoString updatePathStr;
1266   updatePathStr.Assign(updatePath.get());
1267   updRoot->InitWithPath(updatePathStr);
1268 #endif  // XP_WIN
1269   updRoot.forget(aResult);
1270   return NS_OK;
1271 }
1272 
GetProfileStartupDir(nsIFile ** aResult)1273 nsresult nsXREDirProvider::GetProfileStartupDir(nsIFile** aResult) {
1274   if (mProfileDir) return mProfileDir->Clone(aResult);
1275 
1276   if (mAppProvider) {
1277     nsCOMPtr<nsIFile> needsclone;
1278     bool dummy;
1279     nsresult rv = mAppProvider->GetFile(NS_APP_PROFILE_DIR_STARTUP, &dummy,
1280                                         getter_AddRefs(needsclone));
1281     if (NS_SUCCEEDED(rv)) return needsclone->Clone(aResult);
1282   }
1283 
1284   return NS_ERROR_FAILURE;
1285 }
1286 
GetProfileDir(nsIFile ** aResult)1287 nsresult nsXREDirProvider::GetProfileDir(nsIFile** aResult) {
1288   if (mProfileDir) {
1289     if (!mProfileNotified) return NS_ERROR_FAILURE;
1290 
1291     return mProfileDir->Clone(aResult);
1292   }
1293 
1294   if (mAppProvider) {
1295     nsCOMPtr<nsIFile> needsclone;
1296     bool dummy;
1297     nsresult rv = mAppProvider->GetFile(NS_APP_USER_PROFILE_50_DIR, &dummy,
1298                                         getter_AddRefs(needsclone));
1299     if (NS_SUCCEEDED(rv)) return needsclone->Clone(aResult);
1300   }
1301 
1302   return NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, aResult);
1303 }
1304 
1305 NS_IMETHODIMP
SetUserDataDirectory(nsIFile * aFile,bool aLocal)1306 nsXREDirProvider::SetUserDataDirectory(nsIFile* aFile, bool aLocal) {
1307   if (aLocal) {
1308     NS_IF_RELEASE(gDataDirHomeLocal);
1309     NS_IF_ADDREF(gDataDirHomeLocal = aFile);
1310   } else {
1311     NS_IF_RELEASE(gDataDirHome);
1312     NS_IF_ADDREF(gDataDirHome = aFile);
1313   }
1314 
1315   return NS_OK;
1316 }
1317 
1318 /* static */
SetUserDataProfileDirectory(nsCOMPtr<nsIFile> & aFile,bool aLocal)1319 nsresult nsXREDirProvider::SetUserDataProfileDirectory(nsCOMPtr<nsIFile>& aFile,
1320                                                        bool aLocal) {
1321   if (aLocal) {
1322     gDataDirProfileLocal = aFile;
1323   } else {
1324     gDataDirProfile = aFile;
1325   }
1326 
1327   return NS_OK;
1328 }
1329 
GetUserDataDirectoryHome(nsIFile ** aFile,bool aLocal)1330 nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile,
1331                                                     bool aLocal) {
1332   // Copied from nsAppFileLocationProvider (more or less)
1333   nsresult rv;
1334   nsCOMPtr<nsIFile> localDir;
1335 
1336   if (aLocal && gDataDirHomeLocal) {
1337     return gDataDirHomeLocal->Clone(aFile);
1338   }
1339   if (!aLocal && gDataDirHome) {
1340     return gDataDirHome->Clone(aFile);
1341   }
1342 
1343 #if defined(XP_MACOSX)
1344   FSRef fsRef;
1345   OSType folderType;
1346   if (aLocal) {
1347     folderType = kCachedDataFolderType;
1348   } else {
1349 #  ifdef MOZ_THUNDERBIRD
1350     folderType = kDomainLibraryFolderType;
1351 #  else
1352     folderType = kApplicationSupportFolderType;
1353 #  endif
1354   }
1355   OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef);
1356   NS_ENSURE_FALSE(err, NS_ERROR_FAILURE);
1357 
1358   rv = NS_NewNativeLocalFile(""_ns, true, getter_AddRefs(localDir));
1359   NS_ENSURE_SUCCESS(rv, rv);
1360 
1361   nsCOMPtr<nsILocalFileMac> dirFileMac = do_QueryInterface(localDir);
1362   NS_ENSURE_TRUE(dirFileMac, NS_ERROR_UNEXPECTED);
1363 
1364   rv = dirFileMac->InitWithFSRef(&fsRef);
1365   NS_ENSURE_SUCCESS(rv, rv);
1366 
1367   localDir = dirFileMac;
1368 #elif defined(XP_IOS)
1369   nsAutoCString userDir;
1370   if (GetUIKitDirectory(aLocal, userDir)) {
1371     rv = NS_NewNativeLocalFile(userDir, true, getter_AddRefs(localDir));
1372   } else {
1373     rv = NS_ERROR_FAILURE;
1374   }
1375   NS_ENSURE_SUCCESS(rv, rv);
1376 #elif defined(XP_WIN)
1377   nsString path;
1378   if (aLocal) {
1379     rv = GetShellFolderPath(FOLDERID_LocalAppData, path);
1380     if (NS_FAILED(rv)) rv = GetRegWindowsAppDataFolder(aLocal, path);
1381   }
1382   if (!aLocal || NS_FAILED(rv)) {
1383     rv = GetShellFolderPath(FOLDERID_RoamingAppData, path);
1384     if (NS_FAILED(rv)) {
1385       if (!aLocal) rv = GetRegWindowsAppDataFolder(aLocal, path);
1386     }
1387   }
1388   NS_ENSURE_SUCCESS(rv, rv);
1389 
1390   rv = NS_NewLocalFile(path, true, getter_AddRefs(localDir));
1391 #elif defined(XP_UNIX)
1392   const char* homeDir = getenv("HOME");
1393   if (!homeDir || !*homeDir) return NS_ERROR_FAILURE;
1394 
1395 #  ifdef ANDROID /* We want (ProfD == ProfLD) on Android. */
1396   aLocal = false;
1397 #  endif
1398 
1399   if (aLocal) {
1400     // If $XDG_CACHE_HOME is defined use it, otherwise use $HOME/.cache.
1401     const char* cacheHome = getenv("XDG_CACHE_HOME");
1402     if (cacheHome && *cacheHome) {
1403       rv = NS_NewNativeLocalFile(nsDependentCString(cacheHome), true,
1404                                  getter_AddRefs(localDir));
1405     } else {
1406       rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true,
1407                                  getter_AddRefs(localDir));
1408       if (NS_SUCCEEDED(rv)) rv = localDir->AppendNative(".cache"_ns);
1409     }
1410   } else {
1411     rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true,
1412                                getter_AddRefs(localDir));
1413   }
1414 #else
1415 #  error "Don't know how to get product dir on your platform"
1416 #endif
1417 
1418   NS_IF_ADDREF(*aFile = localDir);
1419   return rv;
1420 }
1421 
GetSysUserExtensionsDirectory(nsIFile ** aFile)1422 nsresult nsXREDirProvider::GetSysUserExtensionsDirectory(nsIFile** aFile) {
1423   nsCOMPtr<nsIFile> localDir;
1424   nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false);
1425   NS_ENSURE_SUCCESS(rv, rv);
1426 
1427   rv = AppendSysUserExtensionPath(localDir);
1428   NS_ENSURE_SUCCESS(rv, rv);
1429 
1430   rv = EnsureDirectoryExists(localDir);
1431   NS_ENSURE_SUCCESS(rv, rv);
1432 
1433   localDir.forget(aFile);
1434   return NS_OK;
1435 }
1436 
1437 #if defined(XP_UNIX) || defined(XP_MACOSX)
GetSystemExtensionsDirectory(nsIFile ** aFile)1438 nsresult nsXREDirProvider::GetSystemExtensionsDirectory(nsIFile** aFile) {
1439   nsresult rv;
1440   nsCOMPtr<nsIFile> localDir;
1441 
1442   rv = GetSystemParentDirectory(getter_AddRefs(localDir));
1443   if (NS_SUCCEEDED(rv)) {
1444     constexpr auto sExtensions =
1445 #  if defined(XP_MACOSX)
1446         "Extensions"_ns
1447 #  else
1448         "extensions"_ns
1449 #  endif
1450         ;
1451 
1452     rv = localDir->AppendNative(sExtensions);
1453     if (NS_SUCCEEDED(rv)) {
1454       localDir.forget(aFile);
1455     }
1456   }
1457   return rv;
1458 }
1459 #endif
1460 
GetUserDataDirectory(nsIFile ** aFile,bool aLocal)1461 nsresult nsXREDirProvider::GetUserDataDirectory(nsIFile** aFile, bool aLocal) {
1462   nsCOMPtr<nsIFile> localDir;
1463 
1464   if (aLocal && gDataDirProfileLocal) {
1465     return gDataDirProfileLocal->Clone(aFile);
1466   }
1467   if (!aLocal && gDataDirProfile) {
1468     return gDataDirProfile->Clone(aFile);
1469   }
1470 
1471   nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), aLocal);
1472   NS_ENSURE_SUCCESS(rv, rv);
1473 
1474   rv = AppendProfilePath(localDir, aLocal);
1475   NS_ENSURE_SUCCESS(rv, rv);
1476 
1477   rv = EnsureDirectoryExists(localDir);
1478   NS_ENSURE_SUCCESS(rv, rv);
1479 
1480   nsXREDirProvider::SetUserDataProfileDirectory(localDir, aLocal);
1481 
1482   localDir.forget(aFile);
1483   return NS_OK;
1484 }
1485 
EnsureDirectoryExists(nsIFile * aDirectory)1486 nsresult nsXREDirProvider::EnsureDirectoryExists(nsIFile* aDirectory) {
1487   nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0700);
1488 
1489   if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
1490     rv = NS_OK;
1491   }
1492   return rv;
1493 }
1494 
AppendSysUserExtensionPath(nsIFile * aFile)1495 nsresult nsXREDirProvider::AppendSysUserExtensionPath(nsIFile* aFile) {
1496   NS_ASSERTION(aFile, "Null pointer!");
1497 
1498   nsresult rv;
1499 
1500 #if defined(XP_MACOSX) || defined(XP_WIN)
1501 
1502   static const char* const sXR = "Mozilla";
1503   rv = aFile->AppendNative(nsDependentCString(sXR));
1504   NS_ENSURE_SUCCESS(rv, rv);
1505 
1506   static const char* const sExtensions = "Extensions";
1507   rv = aFile->AppendNative(nsDependentCString(sExtensions));
1508   NS_ENSURE_SUCCESS(rv, rv);
1509 
1510 #elif defined(XP_UNIX)
1511 
1512   static const char* const sXR = ".mozilla";
1513   rv = aFile->AppendNative(nsDependentCString(sXR));
1514   NS_ENSURE_SUCCESS(rv, rv);
1515 
1516   static const char* const sExtensions = "extensions";
1517   rv = aFile->AppendNative(nsDependentCString(sExtensions));
1518   NS_ENSURE_SUCCESS(rv, rv);
1519 
1520 #else
1521 #  error "Don't know how to get XRE user extension path on your platform"
1522 #endif
1523   return NS_OK;
1524 }
1525 
AppendProfilePath(nsIFile * aFile,bool aLocal)1526 nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) {
1527   NS_ASSERTION(aFile, "Null pointer!");
1528 
1529   // If there is no XREAppData then there is no information to use to build
1530   // the profile path so just do nothing. This should only happen in xpcshell
1531   // tests.
1532   if (!gAppData) {
1533     return NS_OK;
1534   }
1535 
1536   nsAutoCString profile;
1537   nsAutoCString appName;
1538   nsAutoCString vendor;
1539   if (gAppData->profile) {
1540     profile = gAppData->profile;
1541   } else {
1542     appName = gAppData->name;
1543     vendor = gAppData->vendor;
1544   }
1545 
1546   nsresult rv = NS_OK;
1547 
1548 #if defined(XP_MACOSX)
1549   if (!profile.IsEmpty()) {
1550     rv = AppendProfileString(aFile, profile.get());
1551   } else {
1552     // Note that MacOS ignores the vendor when creating the profile hierarchy -
1553     // all application preferences directories live alongside one another in
1554     // ~/Library/Application Support/
1555     rv = aFile->AppendNative(appName);
1556   }
1557   NS_ENSURE_SUCCESS(rv, rv);
1558 
1559 #elif defined(XP_WIN)
1560   if (!profile.IsEmpty()) {
1561     rv = AppendProfileString(aFile, profile.get());
1562   } else {
1563     if (!vendor.IsEmpty()) {
1564       rv = aFile->AppendNative(vendor);
1565       NS_ENSURE_SUCCESS(rv, rv);
1566     }
1567     rv = aFile->AppendNative(appName);
1568   }
1569   NS_ENSURE_SUCCESS(rv, rv);
1570 
1571 #elif defined(ANDROID)
1572   // The directory used for storing profiles
1573   // The parent of this directory is set in GetUserDataDirectoryHome
1574   // XXX: handle gAppData->profile properly
1575   // XXXsmaug ...and the rest of the profile creation!
1576   rv = aFile->AppendNative(nsDependentCString("mozilla"));
1577   NS_ENSURE_SUCCESS(rv, rv);
1578 #elif defined(XP_UNIX)
1579   nsAutoCString folder;
1580   // Make it hidden (by starting with "."), except when local (the
1581   // profile is already under ~/.cache or XDG_CACHE_HOME).
1582   if (!aLocal) folder.Assign('.');
1583 
1584   if (!profile.IsEmpty()) {
1585     // Skip any leading path characters
1586     const char* profileStart = profile.get();
1587     while (*profileStart == '/' || *profileStart == '\\') profileStart++;
1588 
1589     // On the off chance that someone wanted their folder to be hidden don't
1590     // let it become ".."
1591     if (*profileStart == '.' && !aLocal) profileStart++;
1592 
1593     folder.Append(profileStart);
1594     ToLowerCase(folder);
1595 
1596     rv = AppendProfileString(aFile, folder.BeginReading());
1597   } else {
1598     if (!vendor.IsEmpty()) {
1599       folder.Append(vendor);
1600       ToLowerCase(folder);
1601 
1602       rv = aFile->AppendNative(folder);
1603       NS_ENSURE_SUCCESS(rv, rv);
1604 
1605       folder.Truncate();
1606     }
1607 
1608     // This can be the case in tests.
1609     if (!appName.IsEmpty()) {
1610       folder.Append(appName);
1611       ToLowerCase(folder);
1612 
1613       rv = aFile->AppendNative(folder);
1614     }
1615   }
1616   NS_ENSURE_SUCCESS(rv, rv);
1617 
1618 #else
1619 #  error "Don't know how to get profile path on your platform"
1620 #endif
1621   return NS_OK;
1622 }
1623 
AppendProfileString(nsIFile * aFile,const char * aPath)1624 nsresult nsXREDirProvider::AppendProfileString(nsIFile* aFile,
1625                                                const char* aPath) {
1626   NS_ASSERTION(aFile, "Null file!");
1627   NS_ASSERTION(aPath, "Null path!");
1628 
1629   nsAutoCString pathDup(aPath);
1630 
1631   char* path = pathDup.BeginWriting();
1632 
1633   nsresult rv;
1634   char* subdir;
1635   while ((subdir = NS_strtok("/\\", &path))) {
1636     rv = aFile->AppendNative(nsDependentCString(subdir));
1637     NS_ENSURE_SUCCESS(rv, rv);
1638   }
1639 
1640   return NS_OK;
1641 }
1642