Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/. */
6 #include "mozilla/ArrayUtils.h"
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <prprf.h>
11 #include <prtime.h>
12 #include "nsProfileLock.h"
14 #ifdef XP_WIN
15 #include <windows.h>
16 #include <shlobj.h>
17 #endif
18 #ifdef XP_UNIX
19 #include <unistd.h>
20 #endif
22 #include "nsIToolkitProfileService.h"
23 #include "nsIToolkitProfile.h"
24 #include "nsIFactory.h"
25 #include "nsIFile.h"
26 #include "nsISimpleEnumerator.h"
28 #ifdef XP_MACOSX
29 #include <CoreFoundation/CoreFoundation.h>
30 #include "nsILocalFileMac.h"
31 #endif
33 #include "nsAppDirectoryServiceDefs.h"
34 #include "nsXULAppAPI.h"
36 #include "nsINIParser.h"
37 #include "nsXREDirProvider.h"
38 #include "nsAppRunner.h"
39 #include "nsString.h"
40 #include "nsReadableUtils.h"
41 #include "nsNativeCharsetUtils.h"
42 #include "mozilla/Attributes.h"
44 using namespace mozilla;
46 class nsToolkitProfile MOZ_FINAL : public nsIToolkitProfile
47 {
48 public:
49 NS_DECL_ISUPPORTS
50 NS_DECL_NSITOOLKITPROFILE
52 friend class nsToolkitProfileService;
53 nsRefPtr<nsToolkitProfile> mNext;
54 nsToolkitProfile *mPrev;
56 ~nsToolkitProfile() { }
58 private:
59 nsToolkitProfile(const nsACString& aName,
60 nsIFile* aRootDir,
61 nsIFile* aLocalDir,
62 nsToolkitProfile* aPrev,
63 bool aForExternalApp);
65 friend class nsToolkitProfileLock;
67 nsCString mName;
68 nsCOMPtr<nsIFile> mRootDir;
69 nsCOMPtr<nsIFile> mLocalDir;
70 nsIProfileLock* mLock;
71 bool mForExternalApp;
72 };
74 class nsToolkitProfileLock MOZ_FINAL : public nsIProfileLock
75 {
76 public:
77 NS_DECL_ISUPPORTS
78 NS_DECL_NSIPROFILELOCK
80 nsresult Init(nsToolkitProfile* aProfile, nsIProfileUnlocker* *aUnlocker);
81 nsresult Init(nsIFile* aDirectory, nsIFile* aLocalDirectory,
82 nsIProfileUnlocker* *aUnlocker);
84 nsToolkitProfileLock() { }
85 ~nsToolkitProfileLock();
87 private:
88 nsRefPtr<nsToolkitProfile> mProfile;
89 nsCOMPtr<nsIFile> mDirectory;
90 nsCOMPtr<nsIFile> mLocalDirectory;
92 nsProfileLock mLock;
93 };
95 class nsToolkitProfileFactory MOZ_FINAL : public nsIFactory
96 {
97 public:
98 NS_DECL_ISUPPORTS
99 NS_DECL_NSIFACTORY
100 };
102 class nsToolkitProfileService MOZ_FINAL : public nsIToolkitProfileService
103 {
104 public:
105 NS_DECL_ISUPPORTS
106 NS_DECL_NSITOOLKITPROFILESERVICE
108 private:
109 friend class nsToolkitProfile;
110 friend class nsToolkitProfileFactory;
111 friend nsresult NS_NewToolkitProfileService(nsIToolkitProfileService**);
113 nsToolkitProfileService() :
114 mDirty(false),
115 mStartWithLast(true),
116 mStartOffline(false)
117 {
118 gService = this;
119 }
120 ~nsToolkitProfileService()
121 {
122 gService = nullptr;
123 }
125 NS_HIDDEN_(nsresult) Init();
127 nsresult CreateTimesInternal(nsIFile *profileDir);
129 nsresult CreateProfileInternal(nsIFile* aRootDir,
130 const nsACString& aName,
131 const nsACString* aProfileName,
132 const nsACString* aAppName,
133 const nsACString* aVendorName,
134 /*in*/ nsIFile** aProfileDefaultsDir,
135 bool aForExternalApp,
136 nsIToolkitProfile** aResult);
138 nsRefPtr<nsToolkitProfile> mFirst;
139 nsCOMPtr<nsIToolkitProfile> mChosen;
140 nsCOMPtr<nsIFile> mAppData;
141 nsCOMPtr<nsIFile> mTempData;
142 nsCOMPtr<nsIFile> mListFile;
143 bool mDirty;
144 bool mStartWithLast;
145 bool mStartOffline;
147 static nsToolkitProfileService *gService;
149 class ProfileEnumerator MOZ_FINAL : public nsISimpleEnumerator
150 {
151 public:
152 NS_DECL_ISUPPORTS
153 NS_DECL_NSISIMPLEENUMERATOR
155 ProfileEnumerator(nsToolkitProfile *first)
156 { mCurrent = first; }
157 private:
158 ~ProfileEnumerator() { }
159 nsRefPtr<nsToolkitProfile> mCurrent;
160 };
161 };
163 nsToolkitProfile::nsToolkitProfile(const nsACString& aName,
164 nsIFile* aRootDir,
165 nsIFile* aLocalDir,
166 nsToolkitProfile* aPrev,
167 bool aForExternalApp) :
168 mPrev(aPrev),
169 mName(aName),
170 mRootDir(aRootDir),
171 mLocalDir(aLocalDir),
172 mLock(nullptr),
173 mForExternalApp(aForExternalApp)
174 {
175 NS_ASSERTION(aRootDir, "No file!");
177 if (!aForExternalApp) {
178 if (aPrev) {
179 aPrev->mNext = this;
180 } else {
181 nsToolkitProfileService::gService->mFirst = this;
182 }
183 }
184 }
186 NS_IMPL_ISUPPORTS(nsToolkitProfile, nsIToolkitProfile)
188 NS_IMETHODIMP
189 nsToolkitProfile::GetRootDir(nsIFile* *aResult)
190 {
191 NS_ADDREF(*aResult = mRootDir);
192 return NS_OK;
193 }
195 NS_IMETHODIMP
196 nsToolkitProfile::GetLocalDir(nsIFile* *aResult)
197 {
198 NS_ADDREF(*aResult = mLocalDir);
199 return NS_OK;
200 }
202 NS_IMETHODIMP
203 nsToolkitProfile::GetName(nsACString& aResult)
204 {
205 aResult = mName;
206 return NS_OK;
207 }
209 NS_IMETHODIMP
210 nsToolkitProfile::SetName(const nsACString& aName)
211 {
212 NS_ASSERTION(nsToolkitProfileService::gService,
213 "Where did my service go?");
214 NS_ENSURE_TRUE(!mForExternalApp, NS_ERROR_NOT_IMPLEMENTED);
216 mName = aName;
217 nsToolkitProfileService::gService->mDirty = true;
219 return NS_OK;
220 }
222 NS_IMETHODIMP
223 nsToolkitProfile::Remove(bool removeFiles)
224 {
225 NS_ASSERTION(nsToolkitProfileService::gService,
226 "Whoa, my service is gone.");
228 NS_ENSURE_TRUE(!mForExternalApp, NS_ERROR_NOT_IMPLEMENTED);
230 if (mLock)
231 return NS_ERROR_FILE_IS_LOCKED;
233 if (!mPrev && !mNext && nsToolkitProfileService::gService->mFirst != this)
234 return NS_ERROR_NOT_INITIALIZED;
236 if (removeFiles) {
237 bool equals;
238 nsresult rv = mRootDir->Equals(mLocalDir, &equals);
239 if (NS_FAILED(rv))
240 return rv;
242 // The root dir might contain the temp dir, so remove
243 // the temp dir first.
244 if (!equals)
245 mLocalDir->Remove(true);
247 mRootDir->Remove(true);
248 }
250 if (mPrev)
251 mPrev->mNext = mNext;
252 else
253 nsToolkitProfileService::gService->mFirst = mNext;
255 if (mNext)
256 mNext->mPrev = mPrev;
258 mPrev = nullptr;
259 mNext = nullptr;
261 if (nsToolkitProfileService::gService->mChosen == this)
262 nsToolkitProfileService::gService->mChosen = nullptr;
264 nsToolkitProfileService::gService->mDirty = true;
266 return NS_OK;
267 }
269 NS_IMETHODIMP
270 nsToolkitProfile::Lock(nsIProfileUnlocker* *aUnlocker, nsIProfileLock* *aResult)
271 {
272 if (mLock) {
273 NS_ADDREF(*aResult = mLock);
274 return NS_OK;
275 }
277 nsRefPtr<nsToolkitProfileLock> lock = new nsToolkitProfileLock();
278 if (!lock) return NS_ERROR_OUT_OF_MEMORY;
280 nsresult rv = lock->Init(this, aUnlocker);
281 if (NS_FAILED(rv)) return rv;
283 NS_ADDREF(*aResult = lock);
284 return NS_OK;
285 }
287 NS_IMPL_ISUPPORTS(nsToolkitProfileLock, nsIProfileLock)
289 nsresult
290 nsToolkitProfileLock::Init(nsToolkitProfile* aProfile, nsIProfileUnlocker* *aUnlocker)
291 {
292 nsresult rv;
293 rv = Init(aProfile->mRootDir, aProfile->mLocalDir, aUnlocker);
294 if (NS_SUCCEEDED(rv))
295 mProfile = aProfile;
297 return rv;
298 }
300 nsresult
301 nsToolkitProfileLock::Init(nsIFile* aDirectory, nsIFile* aLocalDirectory,
302 nsIProfileUnlocker* *aUnlocker)
303 {
304 nsresult rv;
306 rv = mLock.Lock(aDirectory, aUnlocker);
308 if (NS_SUCCEEDED(rv)) {
309 mDirectory = aDirectory;
310 mLocalDirectory = aLocalDirectory;
311 }
313 return rv;
314 }
316 NS_IMETHODIMP
317 nsToolkitProfileLock::GetDirectory(nsIFile* *aResult)
318 {
319 if (!mDirectory) {
320 NS_ERROR("Not initialized, or unlocked!");
321 return NS_ERROR_NOT_INITIALIZED;
322 }
324 NS_ADDREF(*aResult = mDirectory);
325 return NS_OK;
326 }
328 NS_IMETHODIMP
329 nsToolkitProfileLock::GetLocalDirectory(nsIFile* *aResult)
330 {
331 if (!mLocalDirectory) {
332 NS_ERROR("Not initialized, or unlocked!");
333 return NS_ERROR_NOT_INITIALIZED;
334 }
336 NS_ADDREF(*aResult = mLocalDirectory);
337 return NS_OK;
338 }
340 NS_IMETHODIMP
341 nsToolkitProfileLock::Unlock()
342 {
343 if (!mDirectory) {
344 NS_ERROR("Unlocking a never-locked nsToolkitProfileLock!");
345 return NS_ERROR_UNEXPECTED;
346 }
348 mLock.Unlock();
350 if (mProfile) {
351 mProfile->mLock = nullptr;
352 mProfile = nullptr;
353 }
354 mDirectory = nullptr;
355 mLocalDirectory = nullptr;
357 return NS_OK;
358 }
360 NS_IMETHODIMP
361 nsToolkitProfileLock::GetReplacedLockTime(PRTime *aResult)
362 {
363 mLock.GetReplacedLockTime(aResult);
364 return NS_OK;
365 }
367 nsToolkitProfileLock::~nsToolkitProfileLock()
368 {
369 if (mDirectory) {
370 Unlock();
371 }
372 }
374 nsToolkitProfileService*
375 nsToolkitProfileService::gService = nullptr;
377 NS_IMPL_ISUPPORTS(nsToolkitProfileService,
378 nsIToolkitProfileService)
380 nsresult
381 nsToolkitProfileService::Init()
382 {
383 NS_ASSERTION(gDirServiceProvider, "No dirserviceprovider!");
384 nsresult rv;
386 rv = gDirServiceProvider->GetUserAppDataDirectory(getter_AddRefs(mAppData));
387 NS_ENSURE_SUCCESS(rv, rv);
389 rv = gDirServiceProvider->GetUserLocalDataDirectory(getter_AddRefs(mTempData));
390 NS_ENSURE_SUCCESS(rv, rv);
392 rv = mAppData->Clone(getter_AddRefs(mListFile));
393 NS_ENSURE_SUCCESS(rv, rv);
395 rv = mListFile->AppendNative(NS_LITERAL_CSTRING("profiles.ini"));
396 NS_ENSURE_SUCCESS(rv, rv);
398 bool exists;
399 rv = mListFile->IsFile(&exists);
400 if (NS_FAILED(rv) || !exists) {
401 return NS_OK;
402 }
404 int64_t size;
405 rv = mListFile->GetFileSize(&size);
406 if (NS_FAILED(rv) || !size) {
407 return NS_OK;
408 }
410 nsINIParser parser;
411 rv = parser.Init(mListFile);
412 // Init does not fail on parsing errors, only on OOM/really unexpected
413 // conditions.
414 if (NS_FAILED(rv))
415 return rv;
417 nsAutoCString buffer;
418 rv = parser.GetString("General", "StartWithLastProfile", buffer);
419 if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("0"))
420 mStartWithLast = false;
422 nsToolkitProfile* currentProfile = nullptr;
424 unsigned int c = 0;
425 for (c = 0; true; ++c) {
426 nsAutoCString profileID("Profile");
427 profileID.AppendInt(c);
429 rv = parser.GetString(profileID.get(), "IsRelative", buffer);
430 if (NS_FAILED(rv)) break;
432 bool isRelative = buffer.EqualsLiteral("1");
434 nsAutoCString filePath;
436 rv = parser.GetString(profileID.get(), "Path", filePath);
437 if (NS_FAILED(rv)) {
438 NS_ERROR("Malformed profiles.ini: Path= not found");
439 continue;
440 }
442 rv = parser.GetString(profileID.get(), "Name", buffer);
443 if (NS_FAILED(rv)) {
444 NS_ERROR("Malformed profiles.ini: Name= not found");
445 continue;
446 }
448 nsCOMPtr<nsIFile> rootDir;
449 rv = NS_NewNativeLocalFile(EmptyCString(), true,
450 getter_AddRefs(rootDir));
451 NS_ENSURE_SUCCESS(rv, rv);
453 if (isRelative) {
454 rv = rootDir->SetRelativeDescriptor(mAppData, filePath);
455 } else {
456 rv = rootDir->SetPersistentDescriptor(filePath);
457 }
458 if (NS_FAILED(rv)) continue;
460 nsCOMPtr<nsIFile> localDir;
461 if (isRelative) {
462 rv = NS_NewNativeLocalFile(EmptyCString(), true,
463 getter_AddRefs(localDir));
464 NS_ENSURE_SUCCESS(rv, rv);
466 rv = localDir->SetRelativeDescriptor(mTempData, filePath);
467 } else {
468 localDir = rootDir;
469 }
471 currentProfile = new nsToolkitProfile(buffer,
472 rootDir, localDir,
473 currentProfile, false);
474 NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY);
476 rv = parser.GetString(profileID.get(), "Default", buffer);
477 if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1"))
478 mChosen = currentProfile;
479 }
480 if (!mChosen && mFirst && !mFirst->mNext) // only one profile
481 mChosen = mFirst;
482 return NS_OK;
483 }
485 NS_IMETHODIMP
486 nsToolkitProfileService::SetStartWithLastProfile(bool aValue)
487 {
488 if (mStartWithLast != aValue) {
489 mStartWithLast = aValue;
490 mDirty = true;
491 }
492 return NS_OK;
493 }
495 NS_IMETHODIMP
496 nsToolkitProfileService::GetStartWithLastProfile(bool *aResult)
497 {
498 *aResult = mStartWithLast;
499 return NS_OK;
500 }
502 NS_IMETHODIMP
503 nsToolkitProfileService::GetStartOffline(bool *aResult)
504 {
505 *aResult = mStartOffline;
506 return NS_OK;
507 }
509 NS_IMETHODIMP
510 nsToolkitProfileService::SetStartOffline(bool aValue)
511 {
512 mStartOffline = aValue;
513 return NS_OK;
514 }
516 NS_IMETHODIMP
517 nsToolkitProfileService::GetProfiles(nsISimpleEnumerator* *aResult)
518 {
519 *aResult = new ProfileEnumerator(this->mFirst);
520 if (!*aResult)
521 return NS_ERROR_OUT_OF_MEMORY;
523 NS_ADDREF(*aResult);
524 return NS_OK;
525 }
527 NS_IMPL_ISUPPORTS(nsToolkitProfileService::ProfileEnumerator,
528 nsISimpleEnumerator)
530 NS_IMETHODIMP
531 nsToolkitProfileService::ProfileEnumerator::HasMoreElements(bool* aResult)
532 {
533 *aResult = mCurrent ? true : false;
534 return NS_OK;
535 }
537 NS_IMETHODIMP
538 nsToolkitProfileService::ProfileEnumerator::GetNext(nsISupports* *aResult)
539 {
540 if (!mCurrent) return NS_ERROR_FAILURE;
542 NS_ADDREF(*aResult = mCurrent);
544 mCurrent = mCurrent->mNext;
545 return NS_OK;
546 }
548 NS_IMETHODIMP
549 nsToolkitProfileService::GetSelectedProfile(nsIToolkitProfile* *aResult)
550 {
551 if (!mChosen && mFirst && !mFirst->mNext) // only one profile
552 mChosen = mFirst;
554 if (!mChosen) return NS_ERROR_FAILURE;
556 NS_ADDREF(*aResult = mChosen);
557 return NS_OK;
558 }
560 NS_IMETHODIMP
561 nsToolkitProfileService::SetSelectedProfile(nsIToolkitProfile* aProfile)
562 {
563 if (mChosen != aProfile) {
564 mChosen = aProfile;
565 mDirty = true;
566 }
567 return NS_OK;
568 }
570 NS_IMETHODIMP
571 nsToolkitProfileService::GetProfileByName(const nsACString& aName,
572 nsIToolkitProfile* *aResult)
573 {
574 nsToolkitProfile* curP = mFirst;
575 while (curP) {
576 if (curP->mName.Equals(aName)) {
577 NS_ADDREF(*aResult = curP);
578 return NS_OK;
579 }
580 curP = curP->mNext;
581 }
583 return NS_ERROR_FAILURE;
584 }
586 NS_IMETHODIMP
587 nsToolkitProfileService::LockProfilePath(nsIFile* aDirectory,
588 nsIFile* aLocalDirectory,
589 nsIProfileLock* *aResult)
590 {
591 return NS_LockProfilePath(aDirectory, aLocalDirectory, nullptr, aResult);
592 }
594 nsresult
595 NS_LockProfilePath(nsIFile* aPath, nsIFile* aTempPath,
596 nsIProfileUnlocker* *aUnlocker, nsIProfileLock* *aResult)
597 {
598 nsRefPtr<nsToolkitProfileLock> lock = new nsToolkitProfileLock();
599 if (!lock) return NS_ERROR_OUT_OF_MEMORY;
601 nsresult rv = lock->Init(aPath, aTempPath, aUnlocker);
602 if (NS_FAILED(rv)) return rv;
604 NS_ADDREF(*aResult = lock);
605 return NS_OK;
606 }
608 static const char kTable[] =
609 { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
610 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
611 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
613 static void SaltProfileName(nsACString& aName)
614 {
615 double fpTime = double(PR_Now());
617 // use 1e-6, granularity of PR_Now() on the mac is seconds
618 srand((unsigned int)(fpTime * 1e-6 + 0.5));
620 char salt[9];
622 int i;
623 for (i = 0; i < 8; ++i)
624 salt[i] = kTable[rand() % ArrayLength(kTable)];
626 salt[8] = '.';
628 aName.Insert(salt, 0, 9);
629 }
631 NS_IMETHODIMP
632 nsToolkitProfileService::CreateDefaultProfileForApp(const nsACString& aProfileName,
633 const nsACString& aAppName,
634 const nsACString& aVendorName,
635 nsIFile* aProfileDefaultsDir,
636 nsIToolkitProfile** aResult)
637 {
638 NS_ENSURE_STATE(!aProfileName.IsEmpty() || !aAppName.IsEmpty());
639 nsCOMPtr<nsIFile> appData;
640 nsresult rv =
641 gDirServiceProvider->GetUserDataDirectory(getter_AddRefs(appData),
642 false,
643 &aProfileName,
644 &aAppName,
645 &aVendorName);
646 NS_ENSURE_SUCCESS(rv, rv);
648 nsCOMPtr<nsIFile> profilesini;
649 appData->Clone(getter_AddRefs(profilesini));
650 rv = profilesini->AppendNative(NS_LITERAL_CSTRING("profiles.ini"));
651 NS_ENSURE_SUCCESS(rv, rv);
653 bool exists = false;
654 profilesini->Exists(&exists);
655 NS_ENSURE_FALSE(exists, NS_ERROR_ALREADY_INITIALIZED);
657 nsIFile* profileDefaultsDir = aProfileDefaultsDir;
658 rv = CreateProfileInternal(nullptr,
659 NS_LITERAL_CSTRING("default"),
660 &aProfileName, &aAppName, &aVendorName,
661 &profileDefaultsDir, true, aResult);
662 NS_ENSURE_SUCCESS(rv, rv);
663 NS_ENSURE_STATE(*aResult);
665 nsCOMPtr<nsIFile> rootDir;
666 (*aResult)->GetRootDir(getter_AddRefs(rootDir));
667 NS_ENSURE_SUCCESS(rv, rv);
669 nsAutoCString profileDir;
670 rv = rootDir->GetRelativeDescriptor(appData, profileDir);
671 NS_ENSURE_SUCCESS(rv, rv);
673 nsCString ini;
674 ini.SetCapacity(512);
675 ini.AppendLiteral("[General]\n");
676 ini.AppendLiteral("StartWithLastProfile=1\n\n");
678 ini.AppendLiteral("[Profile0]\n");
679 ini.AppendLiteral("Name=default\n");
680 ini.AppendLiteral("IsRelative=1\n");
681 ini.AppendLiteral("Path=");
682 ini.Append(profileDir);
683 ini.Append('\n');
684 ini.AppendLiteral("Default=1\n\n");
686 FILE* writeFile;
687 rv = profilesini->OpenANSIFileDesc("w", &writeFile);
688 NS_ENSURE_SUCCESS(rv, rv);
690 if (fwrite(ini.get(), sizeof(char), ini.Length(), writeFile) !=
691 ini.Length()) {
692 rv = NS_ERROR_UNEXPECTED;
693 }
694 fclose(writeFile);
695 return rv;
696 }
698 NS_IMETHODIMP
699 nsToolkitProfileService::CreateProfile(nsIFile* aRootDir,
700 const nsACString& aName,
701 nsIToolkitProfile** aResult)
702 {
703 return CreateProfileInternal(aRootDir, aName,
704 nullptr, nullptr, nullptr, nullptr, false, aResult);
705 }
707 nsresult
708 nsToolkitProfileService::CreateProfileInternal(nsIFile* aRootDir,
709 const nsACString& aName,
710 const nsACString* aProfileName,
711 const nsACString* aAppName,
712 const nsACString* aVendorName,
713 nsIFile** aProfileDefaultsDir,
714 bool aForExternalApp,
715 nsIToolkitProfile** aResult)
716 {
717 nsresult rv = NS_ERROR_FAILURE;
719 if (!aForExternalApp) {
720 rv = GetProfileByName(aName, aResult);
721 if (NS_SUCCEEDED(rv)) {
722 return rv;
723 }
724 }
726 nsCOMPtr<nsIFile> rootDir (aRootDir);
728 nsAutoCString dirName;
729 if (!rootDir) {
730 rv = gDirServiceProvider->GetUserProfilesRootDir(getter_AddRefs(rootDir),
731 aProfileName, aAppName,
732 aVendorName);
733 NS_ENSURE_SUCCESS(rv, rv);
735 dirName = aName;
736 SaltProfileName(dirName);
738 if (NS_IsNativeUTF8()) {
739 rootDir->AppendNative(dirName);
740 } else {
741 rootDir->Append(NS_ConvertUTF8toUTF16(dirName));
742 }
743 }
745 nsCOMPtr<nsIFile> localDir;
747 bool isRelative;
748 rv = mAppData->Contains(rootDir, true, &isRelative);
749 if (NS_SUCCEEDED(rv) && isRelative) {
750 nsAutoCString path;
751 rv = rootDir->GetRelativeDescriptor(mAppData, path);
752 NS_ENSURE_SUCCESS(rv, rv);
754 rv = NS_NewNativeLocalFile(EmptyCString(), true,
755 getter_AddRefs(localDir));
756 NS_ENSURE_SUCCESS(rv, rv);
758 rv = localDir->SetRelativeDescriptor(mTempData, path);
759 } else {
760 localDir = rootDir;
761 }
763 bool exists;
764 rv = rootDir->Exists(&exists);
765 NS_ENSURE_SUCCESS(rv, rv);
767 if (exists) {
768 rv = rootDir->IsDirectory(&exists);
769 NS_ENSURE_SUCCESS(rv, rv);
771 if (!exists)
772 return NS_ERROR_FILE_NOT_DIRECTORY;
773 }
774 else {
775 nsCOMPtr<nsIFile> profileDefaultsDir;
776 nsCOMPtr<nsIFile> profileDirParent;
777 nsAutoString profileDirName;
779 rv = rootDir->GetParent(getter_AddRefs(profileDirParent));
780 NS_ENSURE_SUCCESS(rv, rv);
782 rv = rootDir->GetLeafName(profileDirName);
783 NS_ENSURE_SUCCESS(rv, rv);
785 if (aProfileDefaultsDir) {
786 profileDefaultsDir = *aProfileDefaultsDir;
787 } else {
788 bool dummy;
789 rv = gDirServiceProvider->GetFile(NS_APP_PROFILE_DEFAULTS_50_DIR, &dummy,
790 getter_AddRefs(profileDefaultsDir));
791 }
793 if (NS_SUCCEEDED(rv) && profileDefaultsDir)
794 rv = profileDefaultsDir->CopyTo(profileDirParent,
795 profileDirName);
796 if (NS_FAILED(rv) || !profileDefaultsDir) {
797 // if copying failed, lets just ensure that the profile directory exists.
798 rv = rootDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
799 NS_ENSURE_SUCCESS(rv, rv);
800 }
801 rv = rootDir->SetPermissions(0700);
802 #ifndef ANDROID
803 // If the profile is on the sdcard, this will fail but its non-fatal
804 NS_ENSURE_SUCCESS(rv, rv);
805 #endif
806 }
808 rv = localDir->Exists(&exists);
809 NS_ENSURE_SUCCESS(rv, rv);
811 if (!exists) {
812 rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
813 NS_ENSURE_SUCCESS(rv, rv);
814 }
816 // We created a new profile dir. Let's store a creation timestamp.
817 // Note that this code path does not apply if the profile dir was
818 // created prior to launching.
819 rv = CreateTimesInternal(rootDir);
820 NS_ENSURE_SUCCESS(rv, rv);
822 nsToolkitProfile* last = aForExternalApp ? nullptr : mFirst;
823 if (last) {
824 while (last->mNext)
825 last = last->mNext;
826 }
828 nsCOMPtr<nsIToolkitProfile> profile =
829 new nsToolkitProfile(aName, rootDir, localDir, last, aForExternalApp);
830 if (!profile) return NS_ERROR_OUT_OF_MEMORY;
832 NS_ADDREF(*aResult = profile);
833 return NS_OK;
834 }
836 nsresult
837 nsToolkitProfileService::CreateTimesInternal(nsIFile* aProfileDir)
838 {
839 nsresult rv = NS_ERROR_FAILURE;
840 nsCOMPtr<nsIFile> creationLog;
841 rv = aProfileDir->Clone(getter_AddRefs(creationLog));
842 NS_ENSURE_SUCCESS(rv, rv);
844 rv = creationLog->AppendNative(NS_LITERAL_CSTRING("times.json"));
845 NS_ENSURE_SUCCESS(rv, rv);
847 bool exists = false;
848 creationLog->Exists(&exists);
849 if (exists) {
850 return NS_OK;
851 }
853 rv = creationLog->Create(nsIFile::NORMAL_FILE_TYPE, 0700);
854 NS_ENSURE_SUCCESS(rv, rv);
856 // We don't care about microsecond resolution.
857 int64_t msec = PR_Now() / PR_USEC_PER_MSEC;
859 // Write it out.
860 PRFileDesc *writeFile;
861 rv = creationLog->OpenNSPRFileDesc(PR_WRONLY, 0700, &writeFile);
862 NS_ENSURE_SUCCESS(rv, rv);
864 PR_fprintf(writeFile, "{\n\"created\": %lld\n}\n", msec);
865 PR_Close(writeFile);
866 return NS_OK;
867 }
869 NS_IMETHODIMP
870 nsToolkitProfileService::GetProfileCount(uint32_t *aResult)
871 {
872 if (!mFirst)
873 *aResult = 0;
874 else if (! mFirst->mNext)
875 *aResult = 1;
876 else
877 *aResult = 2;
879 return NS_OK;
880 }
882 NS_IMETHODIMP
883 nsToolkitProfileService::Flush()
884 {
885 // Errors during writing might cause unhappy semi-written files.
886 // To avoid this, write the entire thing to a buffer, then write
887 // that buffer to disk.
889 nsresult rv;
890 uint32_t pCount = 0;
891 nsToolkitProfile *cur;
893 for (cur = mFirst; cur != nullptr; cur = cur->mNext)
894 ++pCount;
896 uint32_t length;
897 nsAutoArrayPtr<char> buffer (new char[100+MAXPATHLEN*pCount]);
899 NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
901 char *end = buffer;
903 end += sprintf(end,
904 "[General]\n"
905 "StartWithLastProfile=%s\n\n",
906 mStartWithLast ? "1" : "0");
908 nsAutoCString path;
909 cur = mFirst;
910 pCount = 0;
912 while (cur) {
913 // if the profile dir is relative to appdir...
914 bool isRelative;
915 rv = mAppData->Contains(cur->mRootDir, true, &isRelative);
916 if (NS_SUCCEEDED(rv) && isRelative) {
917 // we use a relative descriptor
918 rv = cur->mRootDir->GetRelativeDescriptor(mAppData, path);
919 } else {
920 // otherwise, a persistent descriptor
921 rv = cur->mRootDir->GetPersistentDescriptor(path);
922 NS_ENSURE_SUCCESS(rv, rv);
923 }
925 end += sprintf(end,
926 "[Profile%u]\n"
927 "Name=%s\n"
928 "IsRelative=%s\n"
929 "Path=%s\n",
930 pCount, cur->mName.get(),
931 isRelative ? "1" : "0", path.get());
933 if (mChosen == cur) {
934 end += sprintf(end, "Default=1\n");
935 }
937 end += sprintf(end, "\n");
939 cur = cur->mNext;
940 ++pCount;
941 }
943 FILE* writeFile;
944 rv = mListFile->OpenANSIFileDesc("w", &writeFile);
945 NS_ENSURE_SUCCESS(rv, rv);
947 if (buffer) {
948 length = end - buffer;
950 if (fwrite(buffer, sizeof(char), length, writeFile) != length) {
951 fclose(writeFile);
952 return NS_ERROR_UNEXPECTED;
953 }
954 }
956 fclose(writeFile);
957 return NS_OK;
958 }
960 NS_IMPL_ISUPPORTS(nsToolkitProfileFactory, nsIFactory)
962 NS_IMETHODIMP
963 nsToolkitProfileFactory::CreateInstance(nsISupports* aOuter, const nsID& aIID,
964 void** aResult)
965 {
966 if (aOuter)
967 return NS_ERROR_NO_AGGREGATION;
969 nsCOMPtr<nsIToolkitProfileService> profileService =
970 nsToolkitProfileService::gService;
971 if (!profileService) {
972 nsresult rv = NS_NewToolkitProfileService(getter_AddRefs(profileService));
973 if (NS_FAILED(rv))
974 return rv;
975 }
976 return profileService->QueryInterface(aIID, aResult);
977 }
979 NS_IMETHODIMP
980 nsToolkitProfileFactory::LockFactory(bool aVal)
981 {
982 return NS_OK;
983 }
985 nsresult
986 NS_NewToolkitProfileFactory(nsIFactory* *aResult)
987 {
988 *aResult = new nsToolkitProfileFactory();
989 if (!*aResult)
990 return NS_ERROR_OUT_OF_MEMORY;
992 NS_ADDREF(*aResult);
993 return NS_OK;
994 }
996 nsresult
997 NS_NewToolkitProfileService(nsIToolkitProfileService* *aResult)
998 {
999 nsToolkitProfileService* profileService = new nsToolkitProfileService();
1000 if (!profileService)
1001 return NS_ERROR_OUT_OF_MEMORY;
1002 nsresult rv = profileService->Init();
1003 if (NS_FAILED(rv)) {
1004 NS_ERROR("nsToolkitProfileService::Init failed!");
1005 delete profileService;
1006 return rv;
1007 }
1009 NS_ADDREF(*aResult = profileService);
1010 return NS_OK;
1011 }
1013 nsresult
1014 XRE_GetFileFromPath(const char *aPath, nsIFile* *aResult)
1015 {
1016 #if defined(XP_MACOSX)
1017 int32_t pathLen = strlen(aPath);
1018 if (pathLen > MAXPATHLEN)
1019 return NS_ERROR_INVALID_ARG;
1021 CFURLRef fullPath =
1022 CFURLCreateFromFileSystemRepresentation(nullptr, (const UInt8 *) aPath,
1023 pathLen, true);
1024 if (!fullPath)
1025 return NS_ERROR_FAILURE;
1027 nsCOMPtr<nsIFile> lf;
1028 nsresult rv = NS_NewNativeLocalFile(EmptyCString(), true,
1029 getter_AddRefs(lf));
1030 if (NS_SUCCEEDED(rv)) {
1031 nsCOMPtr<nsILocalFileMac> lfMac = do_QueryInterface(lf, &rv);
1032 if (NS_SUCCEEDED(rv)) {
1033 rv = lfMac->InitWithCFURL(fullPath);
1034 if (NS_SUCCEEDED(rv))
1035 NS_ADDREF(*aResult = lf);
1036 }
1037 }
1038 CFRelease(fullPath);
1039 return rv;
1041 #elif defined(XP_UNIX)
1042 char fullPath[MAXPATHLEN];
1044 if (!realpath(aPath, fullPath))
1045 return NS_ERROR_FAILURE;
1047 return NS_NewNativeLocalFile(nsDependentCString(fullPath), true,
1048 aResult);
1049 #elif defined(XP_WIN)
1050 WCHAR fullPath[MAXPATHLEN];
1052 if (!_wfullpath(fullPath, NS_ConvertUTF8toUTF16(aPath).get(), MAXPATHLEN))
1053 return NS_ERROR_FAILURE;
1055 return NS_NewLocalFile(nsDependentString(fullPath), true,
1056 aResult);
1058 #else
1059 #error Platform-specific logic needed here.
1060 #endif
1061 }