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