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