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