michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "nsProfileLock.h" michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #include michael@0: #endif michael@0: #ifdef XP_UNIX michael@0: #include michael@0: #endif michael@0: michael@0: #include "nsIToolkitProfileService.h" michael@0: #include "nsIToolkitProfile.h" michael@0: #include "nsIFactory.h" michael@0: #include "nsIFile.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: michael@0: #ifdef XP_MACOSX michael@0: #include michael@0: #include "nsILocalFileMac.h" michael@0: #endif michael@0: michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsXULAppAPI.h" michael@0: michael@0: #include "nsINIParser.h" michael@0: #include "nsXREDirProvider.h" michael@0: #include "nsAppRunner.h" michael@0: #include "nsString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsNativeCharsetUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: class nsToolkitProfile MOZ_FINAL : public nsIToolkitProfile michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSITOOLKITPROFILE michael@0: michael@0: friend class nsToolkitProfileService; michael@0: nsRefPtr mNext; michael@0: nsToolkitProfile *mPrev; michael@0: michael@0: ~nsToolkitProfile() { } michael@0: michael@0: private: michael@0: nsToolkitProfile(const nsACString& aName, michael@0: nsIFile* aRootDir, michael@0: nsIFile* aLocalDir, michael@0: nsToolkitProfile* aPrev, michael@0: bool aForExternalApp); michael@0: michael@0: friend class nsToolkitProfileLock; michael@0: michael@0: nsCString mName; michael@0: nsCOMPtr mRootDir; michael@0: nsCOMPtr mLocalDir; michael@0: nsIProfileLock* mLock; michael@0: bool mForExternalApp; michael@0: }; michael@0: michael@0: class nsToolkitProfileLock MOZ_FINAL : public nsIProfileLock michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIPROFILELOCK michael@0: michael@0: nsresult Init(nsToolkitProfile* aProfile, nsIProfileUnlocker* *aUnlocker); michael@0: nsresult Init(nsIFile* aDirectory, nsIFile* aLocalDirectory, michael@0: nsIProfileUnlocker* *aUnlocker); michael@0: michael@0: nsToolkitProfileLock() { } michael@0: ~nsToolkitProfileLock(); michael@0: michael@0: private: michael@0: nsRefPtr mProfile; michael@0: nsCOMPtr mDirectory; michael@0: nsCOMPtr mLocalDirectory; michael@0: michael@0: nsProfileLock mLock; michael@0: }; michael@0: michael@0: class nsToolkitProfileFactory MOZ_FINAL : public nsIFactory michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIFACTORY michael@0: }; michael@0: michael@0: class nsToolkitProfileService MOZ_FINAL : public nsIToolkitProfileService michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSITOOLKITPROFILESERVICE michael@0: michael@0: private: michael@0: friend class nsToolkitProfile; michael@0: friend class nsToolkitProfileFactory; michael@0: friend nsresult NS_NewToolkitProfileService(nsIToolkitProfileService**); michael@0: michael@0: nsToolkitProfileService() : michael@0: mDirty(false), michael@0: mStartWithLast(true), michael@0: mStartOffline(false) michael@0: { michael@0: gService = this; michael@0: } michael@0: ~nsToolkitProfileService() michael@0: { michael@0: gService = nullptr; michael@0: } michael@0: michael@0: NS_HIDDEN_(nsresult) Init(); michael@0: michael@0: nsresult CreateTimesInternal(nsIFile *profileDir); michael@0: michael@0: nsresult CreateProfileInternal(nsIFile* aRootDir, michael@0: const nsACString& aName, michael@0: const nsACString* aProfileName, michael@0: const nsACString* aAppName, michael@0: const nsACString* aVendorName, michael@0: /*in*/ nsIFile** aProfileDefaultsDir, michael@0: bool aForExternalApp, michael@0: nsIToolkitProfile** aResult); michael@0: michael@0: nsRefPtr mFirst; michael@0: nsCOMPtr mChosen; michael@0: nsCOMPtr mAppData; michael@0: nsCOMPtr mTempData; michael@0: nsCOMPtr mListFile; michael@0: bool mDirty; michael@0: bool mStartWithLast; michael@0: bool mStartOffline; michael@0: michael@0: static nsToolkitProfileService *gService; michael@0: michael@0: class ProfileEnumerator MOZ_FINAL : public nsISimpleEnumerator michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSISIMPLEENUMERATOR michael@0: michael@0: ProfileEnumerator(nsToolkitProfile *first) michael@0: { mCurrent = first; } michael@0: private: michael@0: ~ProfileEnumerator() { } michael@0: nsRefPtr mCurrent; michael@0: }; michael@0: }; michael@0: michael@0: nsToolkitProfile::nsToolkitProfile(const nsACString& aName, michael@0: nsIFile* aRootDir, michael@0: nsIFile* aLocalDir, michael@0: nsToolkitProfile* aPrev, michael@0: bool aForExternalApp) : michael@0: mPrev(aPrev), michael@0: mName(aName), michael@0: mRootDir(aRootDir), michael@0: mLocalDir(aLocalDir), michael@0: mLock(nullptr), michael@0: mForExternalApp(aForExternalApp) michael@0: { michael@0: NS_ASSERTION(aRootDir, "No file!"); michael@0: michael@0: if (!aForExternalApp) { michael@0: if (aPrev) { michael@0: aPrev->mNext = this; michael@0: } else { michael@0: nsToolkitProfileService::gService->mFirst = this; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsToolkitProfile, nsIToolkitProfile) michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfile::GetRootDir(nsIFile* *aResult) michael@0: { michael@0: NS_ADDREF(*aResult = mRootDir); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfile::GetLocalDir(nsIFile* *aResult) michael@0: { michael@0: NS_ADDREF(*aResult = mLocalDir); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfile::GetName(nsACString& aResult) michael@0: { michael@0: aResult = mName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfile::SetName(const nsACString& aName) michael@0: { michael@0: NS_ASSERTION(nsToolkitProfileService::gService, michael@0: "Where did my service go?"); michael@0: NS_ENSURE_TRUE(!mForExternalApp, NS_ERROR_NOT_IMPLEMENTED); michael@0: michael@0: mName = aName; michael@0: nsToolkitProfileService::gService->mDirty = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfile::Remove(bool removeFiles) michael@0: { michael@0: NS_ASSERTION(nsToolkitProfileService::gService, michael@0: "Whoa, my service is gone."); michael@0: michael@0: NS_ENSURE_TRUE(!mForExternalApp, NS_ERROR_NOT_IMPLEMENTED); michael@0: michael@0: if (mLock) michael@0: return NS_ERROR_FILE_IS_LOCKED; michael@0: michael@0: if (!mPrev && !mNext && nsToolkitProfileService::gService->mFirst != this) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: if (removeFiles) { michael@0: bool equals; michael@0: nsresult rv = mRootDir->Equals(mLocalDir, &equals); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // The root dir might contain the temp dir, so remove michael@0: // the temp dir first. michael@0: if (!equals) michael@0: mLocalDir->Remove(true); michael@0: michael@0: mRootDir->Remove(true); michael@0: } michael@0: michael@0: if (mPrev) michael@0: mPrev->mNext = mNext; michael@0: else michael@0: nsToolkitProfileService::gService->mFirst = mNext; michael@0: michael@0: if (mNext) michael@0: mNext->mPrev = mPrev; michael@0: michael@0: mPrev = nullptr; michael@0: mNext = nullptr; michael@0: michael@0: if (nsToolkitProfileService::gService->mChosen == this) michael@0: nsToolkitProfileService::gService->mChosen = nullptr; michael@0: michael@0: nsToolkitProfileService::gService->mDirty = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfile::Lock(nsIProfileUnlocker* *aUnlocker, nsIProfileLock* *aResult) michael@0: { michael@0: if (mLock) { michael@0: NS_ADDREF(*aResult = mLock); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr lock = new nsToolkitProfileLock(); michael@0: if (!lock) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsresult rv = lock->Init(this, aUnlocker); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_ADDREF(*aResult = lock); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsToolkitProfileLock, nsIProfileLock) michael@0: michael@0: nsresult michael@0: nsToolkitProfileLock::Init(nsToolkitProfile* aProfile, nsIProfileUnlocker* *aUnlocker) michael@0: { michael@0: nsresult rv; michael@0: rv = Init(aProfile->mRootDir, aProfile->mLocalDir, aUnlocker); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mProfile = aProfile; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsToolkitProfileLock::Init(nsIFile* aDirectory, nsIFile* aLocalDirectory, michael@0: nsIProfileUnlocker* *aUnlocker) michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = mLock.Lock(aDirectory, aUnlocker); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mDirectory = aDirectory; michael@0: mLocalDirectory = aLocalDirectory; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileLock::GetDirectory(nsIFile* *aResult) michael@0: { michael@0: if (!mDirectory) { michael@0: NS_ERROR("Not initialized, or unlocked!"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: NS_ADDREF(*aResult = mDirectory); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileLock::GetLocalDirectory(nsIFile* *aResult) michael@0: { michael@0: if (!mLocalDirectory) { michael@0: NS_ERROR("Not initialized, or unlocked!"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: NS_ADDREF(*aResult = mLocalDirectory); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileLock::Unlock() michael@0: { michael@0: if (!mDirectory) { michael@0: NS_ERROR("Unlocking a never-locked nsToolkitProfileLock!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: mLock.Unlock(); michael@0: michael@0: if (mProfile) { michael@0: mProfile->mLock = nullptr; michael@0: mProfile = nullptr; michael@0: } michael@0: mDirectory = nullptr; michael@0: mLocalDirectory = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileLock::GetReplacedLockTime(PRTime *aResult) michael@0: { michael@0: mLock.GetReplacedLockTime(aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsToolkitProfileLock::~nsToolkitProfileLock() michael@0: { michael@0: if (mDirectory) { michael@0: Unlock(); michael@0: } michael@0: } michael@0: michael@0: nsToolkitProfileService* michael@0: nsToolkitProfileService::gService = nullptr; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsToolkitProfileService, michael@0: nsIToolkitProfileService) michael@0: michael@0: nsresult michael@0: nsToolkitProfileService::Init() michael@0: { michael@0: NS_ASSERTION(gDirServiceProvider, "No dirserviceprovider!"); michael@0: nsresult rv; michael@0: michael@0: rv = gDirServiceProvider->GetUserAppDataDirectory(getter_AddRefs(mAppData)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = gDirServiceProvider->GetUserLocalDataDirectory(getter_AddRefs(mTempData)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mAppData->Clone(getter_AddRefs(mListFile)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mListFile->AppendNative(NS_LITERAL_CSTRING("profiles.ini")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool exists; michael@0: rv = mListFile->IsFile(&exists); michael@0: if (NS_FAILED(rv) || !exists) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: int64_t size; michael@0: rv = mListFile->GetFileSize(&size); michael@0: if (NS_FAILED(rv) || !size) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsINIParser parser; michael@0: rv = parser.Init(mListFile); michael@0: // Init does not fail on parsing errors, only on OOM/really unexpected michael@0: // conditions. michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsAutoCString buffer; michael@0: rv = parser.GetString("General", "StartWithLastProfile", buffer); michael@0: if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("0")) michael@0: mStartWithLast = false; michael@0: michael@0: nsToolkitProfile* currentProfile = nullptr; michael@0: michael@0: unsigned int c = 0; michael@0: for (c = 0; true; ++c) { michael@0: nsAutoCString profileID("Profile"); michael@0: profileID.AppendInt(c); michael@0: michael@0: rv = parser.GetString(profileID.get(), "IsRelative", buffer); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: bool isRelative = buffer.EqualsLiteral("1"); michael@0: michael@0: nsAutoCString filePath; michael@0: michael@0: rv = parser.GetString(profileID.get(), "Path", filePath); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("Malformed profiles.ini: Path= not found"); michael@0: continue; michael@0: } michael@0: michael@0: rv = parser.GetString(profileID.get(), "Name", buffer); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("Malformed profiles.ini: Name= not found"); michael@0: continue; michael@0: } michael@0: michael@0: nsCOMPtr rootDir; michael@0: rv = NS_NewNativeLocalFile(EmptyCString(), true, michael@0: getter_AddRefs(rootDir)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (isRelative) { michael@0: rv = rootDir->SetRelativeDescriptor(mAppData, filePath); michael@0: } else { michael@0: rv = rootDir->SetPersistentDescriptor(filePath); michael@0: } michael@0: if (NS_FAILED(rv)) continue; michael@0: michael@0: nsCOMPtr localDir; michael@0: if (isRelative) { michael@0: rv = NS_NewNativeLocalFile(EmptyCString(), true, michael@0: getter_AddRefs(localDir)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = localDir->SetRelativeDescriptor(mTempData, filePath); michael@0: } else { michael@0: localDir = rootDir; michael@0: } michael@0: michael@0: currentProfile = new nsToolkitProfile(buffer, michael@0: rootDir, localDir, michael@0: currentProfile, false); michael@0: NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: rv = parser.GetString(profileID.get(), "Default", buffer); michael@0: if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1")) michael@0: mChosen = currentProfile; michael@0: } michael@0: if (!mChosen && mFirst && !mFirst->mNext) // only one profile michael@0: mChosen = mFirst; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::SetStartWithLastProfile(bool aValue) michael@0: { michael@0: if (mStartWithLast != aValue) { michael@0: mStartWithLast = aValue; michael@0: mDirty = true; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::GetStartWithLastProfile(bool *aResult) michael@0: { michael@0: *aResult = mStartWithLast; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::GetStartOffline(bool *aResult) michael@0: { michael@0: *aResult = mStartOffline; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::SetStartOffline(bool aValue) michael@0: { michael@0: mStartOffline = aValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::GetProfiles(nsISimpleEnumerator* *aResult) michael@0: { michael@0: *aResult = new ProfileEnumerator(this->mFirst); michael@0: if (!*aResult) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(*aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsToolkitProfileService::ProfileEnumerator, michael@0: nsISimpleEnumerator) michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::ProfileEnumerator::HasMoreElements(bool* aResult) michael@0: { michael@0: *aResult = mCurrent ? true : false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::ProfileEnumerator::GetNext(nsISupports* *aResult) michael@0: { michael@0: if (!mCurrent) return NS_ERROR_FAILURE; michael@0: michael@0: NS_ADDREF(*aResult = mCurrent); michael@0: michael@0: mCurrent = mCurrent->mNext; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::GetSelectedProfile(nsIToolkitProfile* *aResult) michael@0: { michael@0: if (!mChosen && mFirst && !mFirst->mNext) // only one profile michael@0: mChosen = mFirst; michael@0: michael@0: if (!mChosen) return NS_ERROR_FAILURE; michael@0: michael@0: NS_ADDREF(*aResult = mChosen); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::SetSelectedProfile(nsIToolkitProfile* aProfile) michael@0: { michael@0: if (mChosen != aProfile) { michael@0: mChosen = aProfile; michael@0: mDirty = true; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::GetProfileByName(const nsACString& aName, michael@0: nsIToolkitProfile* *aResult) michael@0: { michael@0: nsToolkitProfile* curP = mFirst; michael@0: while (curP) { michael@0: if (curP->mName.Equals(aName)) { michael@0: NS_ADDREF(*aResult = curP); michael@0: return NS_OK; michael@0: } michael@0: curP = curP->mNext; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::LockProfilePath(nsIFile* aDirectory, michael@0: nsIFile* aLocalDirectory, michael@0: nsIProfileLock* *aResult) michael@0: { michael@0: return NS_LockProfilePath(aDirectory, aLocalDirectory, nullptr, aResult); michael@0: } michael@0: michael@0: nsresult michael@0: NS_LockProfilePath(nsIFile* aPath, nsIFile* aTempPath, michael@0: nsIProfileUnlocker* *aUnlocker, nsIProfileLock* *aResult) michael@0: { michael@0: nsRefPtr lock = new nsToolkitProfileLock(); michael@0: if (!lock) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsresult rv = lock->Init(aPath, aTempPath, aUnlocker); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_ADDREF(*aResult = lock); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static const char kTable[] = michael@0: { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', michael@0: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', michael@0: '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; michael@0: michael@0: static void SaltProfileName(nsACString& aName) michael@0: { michael@0: double fpTime = double(PR_Now()); michael@0: michael@0: // use 1e-6, granularity of PR_Now() on the mac is seconds michael@0: srand((unsigned int)(fpTime * 1e-6 + 0.5)); michael@0: michael@0: char salt[9]; michael@0: michael@0: int i; michael@0: for (i = 0; i < 8; ++i) michael@0: salt[i] = kTable[rand() % ArrayLength(kTable)]; michael@0: michael@0: salt[8] = '.'; michael@0: michael@0: aName.Insert(salt, 0, 9); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::CreateDefaultProfileForApp(const nsACString& aProfileName, michael@0: const nsACString& aAppName, michael@0: const nsACString& aVendorName, michael@0: nsIFile* aProfileDefaultsDir, michael@0: nsIToolkitProfile** aResult) michael@0: { michael@0: NS_ENSURE_STATE(!aProfileName.IsEmpty() || !aAppName.IsEmpty()); michael@0: nsCOMPtr appData; michael@0: nsresult rv = michael@0: gDirServiceProvider->GetUserDataDirectory(getter_AddRefs(appData), michael@0: false, michael@0: &aProfileName, michael@0: &aAppName, michael@0: &aVendorName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr profilesini; michael@0: appData->Clone(getter_AddRefs(profilesini)); michael@0: rv = profilesini->AppendNative(NS_LITERAL_CSTRING("profiles.ini")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool exists = false; michael@0: profilesini->Exists(&exists); michael@0: NS_ENSURE_FALSE(exists, NS_ERROR_ALREADY_INITIALIZED); michael@0: michael@0: nsIFile* profileDefaultsDir = aProfileDefaultsDir; michael@0: rv = CreateProfileInternal(nullptr, michael@0: NS_LITERAL_CSTRING("default"), michael@0: &aProfileName, &aAppName, &aVendorName, michael@0: &profileDefaultsDir, true, aResult); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_ENSURE_STATE(*aResult); michael@0: michael@0: nsCOMPtr rootDir; michael@0: (*aResult)->GetRootDir(getter_AddRefs(rootDir)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString profileDir; michael@0: rv = rootDir->GetRelativeDescriptor(appData, profileDir); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCString ini; michael@0: ini.SetCapacity(512); michael@0: ini.AppendLiteral("[General]\n"); michael@0: ini.AppendLiteral("StartWithLastProfile=1\n\n"); michael@0: michael@0: ini.AppendLiteral("[Profile0]\n"); michael@0: ini.AppendLiteral("Name=default\n"); michael@0: ini.AppendLiteral("IsRelative=1\n"); michael@0: ini.AppendLiteral("Path="); michael@0: ini.Append(profileDir); michael@0: ini.Append('\n'); michael@0: ini.AppendLiteral("Default=1\n\n"); michael@0: michael@0: FILE* writeFile; michael@0: rv = profilesini->OpenANSIFileDesc("w", &writeFile); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (fwrite(ini.get(), sizeof(char), ini.Length(), writeFile) != michael@0: ini.Length()) { michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: } michael@0: fclose(writeFile); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::CreateProfile(nsIFile* aRootDir, michael@0: const nsACString& aName, michael@0: nsIToolkitProfile** aResult) michael@0: { michael@0: return CreateProfileInternal(aRootDir, aName, michael@0: nullptr, nullptr, nullptr, nullptr, false, aResult); michael@0: } michael@0: michael@0: nsresult michael@0: nsToolkitProfileService::CreateProfileInternal(nsIFile* aRootDir, michael@0: const nsACString& aName, michael@0: const nsACString* aProfileName, michael@0: const nsACString* aAppName, michael@0: const nsACString* aVendorName, michael@0: nsIFile** aProfileDefaultsDir, michael@0: bool aForExternalApp, michael@0: nsIToolkitProfile** aResult) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: if (!aForExternalApp) { michael@0: rv = GetProfileByName(aName, aResult); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr rootDir (aRootDir); michael@0: michael@0: nsAutoCString dirName; michael@0: if (!rootDir) { michael@0: rv = gDirServiceProvider->GetUserProfilesRootDir(getter_AddRefs(rootDir), michael@0: aProfileName, aAppName, michael@0: aVendorName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: dirName = aName; michael@0: SaltProfileName(dirName); michael@0: michael@0: if (NS_IsNativeUTF8()) { michael@0: rootDir->AppendNative(dirName); michael@0: } else { michael@0: rootDir->Append(NS_ConvertUTF8toUTF16(dirName)); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr localDir; michael@0: michael@0: bool isRelative; michael@0: rv = mAppData->Contains(rootDir, true, &isRelative); michael@0: if (NS_SUCCEEDED(rv) && isRelative) { michael@0: nsAutoCString path; michael@0: rv = rootDir->GetRelativeDescriptor(mAppData, path); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = NS_NewNativeLocalFile(EmptyCString(), true, michael@0: getter_AddRefs(localDir)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = localDir->SetRelativeDescriptor(mTempData, path); michael@0: } else { michael@0: localDir = rootDir; michael@0: } michael@0: michael@0: bool exists; michael@0: rv = rootDir->Exists(&exists); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (exists) { michael@0: rv = rootDir->IsDirectory(&exists); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!exists) michael@0: return NS_ERROR_FILE_NOT_DIRECTORY; michael@0: } michael@0: else { michael@0: nsCOMPtr profileDefaultsDir; michael@0: nsCOMPtr profileDirParent; michael@0: nsAutoString profileDirName; michael@0: michael@0: rv = rootDir->GetParent(getter_AddRefs(profileDirParent)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = rootDir->GetLeafName(profileDirName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aProfileDefaultsDir) { michael@0: profileDefaultsDir = *aProfileDefaultsDir; michael@0: } else { michael@0: bool dummy; michael@0: rv = gDirServiceProvider->GetFile(NS_APP_PROFILE_DEFAULTS_50_DIR, &dummy, michael@0: getter_AddRefs(profileDefaultsDir)); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv) && profileDefaultsDir) michael@0: rv = profileDefaultsDir->CopyTo(profileDirParent, michael@0: profileDirName); michael@0: if (NS_FAILED(rv) || !profileDefaultsDir) { michael@0: // if copying failed, lets just ensure that the profile directory exists. michael@0: rv = rootDir->Create(nsIFile::DIRECTORY_TYPE, 0700); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: rv = rootDir->SetPermissions(0700); michael@0: #ifndef ANDROID michael@0: // If the profile is on the sdcard, this will fail but its non-fatal michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: #endif michael@0: } michael@0: michael@0: rv = localDir->Exists(&exists); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!exists) { michael@0: rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // We created a new profile dir. Let's store a creation timestamp. michael@0: // Note that this code path does not apply if the profile dir was michael@0: // created prior to launching. michael@0: rv = CreateTimesInternal(rootDir); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsToolkitProfile* last = aForExternalApp ? nullptr : mFirst; michael@0: if (last) { michael@0: while (last->mNext) michael@0: last = last->mNext; michael@0: } michael@0: michael@0: nsCOMPtr profile = michael@0: new nsToolkitProfile(aName, rootDir, localDir, last, aForExternalApp); michael@0: if (!profile) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(*aResult = profile); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsToolkitProfileService::CreateTimesInternal(nsIFile* aProfileDir) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: nsCOMPtr creationLog; michael@0: rv = aProfileDir->Clone(getter_AddRefs(creationLog)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = creationLog->AppendNative(NS_LITERAL_CSTRING("times.json")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool exists = false; michael@0: creationLog->Exists(&exists); michael@0: if (exists) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: rv = creationLog->Create(nsIFile::NORMAL_FILE_TYPE, 0700); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // We don't care about microsecond resolution. michael@0: int64_t msec = PR_Now() / PR_USEC_PER_MSEC; michael@0: michael@0: // Write it out. michael@0: PRFileDesc *writeFile; michael@0: rv = creationLog->OpenNSPRFileDesc(PR_WRONLY, 0700, &writeFile); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: PR_fprintf(writeFile, "{\n\"created\": %lld\n}\n", msec); michael@0: PR_Close(writeFile); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::GetProfileCount(uint32_t *aResult) michael@0: { michael@0: if (!mFirst) michael@0: *aResult = 0; michael@0: else if (! mFirst->mNext) michael@0: *aResult = 1; michael@0: else michael@0: *aResult = 2; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileService::Flush() michael@0: { michael@0: // Errors during writing might cause unhappy semi-written files. michael@0: // To avoid this, write the entire thing to a buffer, then write michael@0: // that buffer to disk. michael@0: michael@0: nsresult rv; michael@0: uint32_t pCount = 0; michael@0: nsToolkitProfile *cur; michael@0: michael@0: for (cur = mFirst; cur != nullptr; cur = cur->mNext) michael@0: ++pCount; michael@0: michael@0: uint32_t length; michael@0: nsAutoArrayPtr buffer (new char[100+MAXPATHLEN*pCount]); michael@0: michael@0: NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: char *end = buffer; michael@0: michael@0: end += sprintf(end, michael@0: "[General]\n" michael@0: "StartWithLastProfile=%s\n\n", michael@0: mStartWithLast ? "1" : "0"); michael@0: michael@0: nsAutoCString path; michael@0: cur = mFirst; michael@0: pCount = 0; michael@0: michael@0: while (cur) { michael@0: // if the profile dir is relative to appdir... michael@0: bool isRelative; michael@0: rv = mAppData->Contains(cur->mRootDir, true, &isRelative); michael@0: if (NS_SUCCEEDED(rv) && isRelative) { michael@0: // we use a relative descriptor michael@0: rv = cur->mRootDir->GetRelativeDescriptor(mAppData, path); michael@0: } else { michael@0: // otherwise, a persistent descriptor michael@0: rv = cur->mRootDir->GetPersistentDescriptor(path); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: end += sprintf(end, michael@0: "[Profile%u]\n" michael@0: "Name=%s\n" michael@0: "IsRelative=%s\n" michael@0: "Path=%s\n", michael@0: pCount, cur->mName.get(), michael@0: isRelative ? "1" : "0", path.get()); michael@0: michael@0: if (mChosen == cur) { michael@0: end += sprintf(end, "Default=1\n"); michael@0: } michael@0: michael@0: end += sprintf(end, "\n"); michael@0: michael@0: cur = cur->mNext; michael@0: ++pCount; michael@0: } michael@0: michael@0: FILE* writeFile; michael@0: rv = mListFile->OpenANSIFileDesc("w", &writeFile); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (buffer) { michael@0: length = end - buffer; michael@0: michael@0: if (fwrite(buffer, sizeof(char), length, writeFile) != length) { michael@0: fclose(writeFile); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: } michael@0: michael@0: fclose(writeFile); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsToolkitProfileFactory, nsIFactory) michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileFactory::CreateInstance(nsISupports* aOuter, const nsID& aIID, michael@0: void** aResult) michael@0: { michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: nsCOMPtr profileService = michael@0: nsToolkitProfileService::gService; michael@0: if (!profileService) { michael@0: nsresult rv = NS_NewToolkitProfileService(getter_AddRefs(profileService)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: return profileService->QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsToolkitProfileFactory::LockFactory(bool aVal) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewToolkitProfileFactory(nsIFactory* *aResult) michael@0: { michael@0: *aResult = new nsToolkitProfileFactory(); michael@0: if (!*aResult) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(*aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewToolkitProfileService(nsIToolkitProfileService* *aResult) michael@0: { michael@0: nsToolkitProfileService* profileService = new nsToolkitProfileService(); michael@0: if (!profileService) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: nsresult rv = profileService->Init(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("nsToolkitProfileService::Init failed!"); michael@0: delete profileService; michael@0: return rv; michael@0: } michael@0: michael@0: NS_ADDREF(*aResult = profileService); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XRE_GetFileFromPath(const char *aPath, nsIFile* *aResult) michael@0: { michael@0: #if defined(XP_MACOSX) michael@0: int32_t pathLen = strlen(aPath); michael@0: if (pathLen > MAXPATHLEN) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: CFURLRef fullPath = michael@0: CFURLCreateFromFileSystemRepresentation(nullptr, (const UInt8 *) aPath, michael@0: pathLen, true); michael@0: if (!fullPath) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr lf; michael@0: nsresult rv = NS_NewNativeLocalFile(EmptyCString(), true, michael@0: getter_AddRefs(lf)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr lfMac = do_QueryInterface(lf, &rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = lfMac->InitWithCFURL(fullPath); michael@0: if (NS_SUCCEEDED(rv)) michael@0: NS_ADDREF(*aResult = lf); michael@0: } michael@0: } michael@0: CFRelease(fullPath); michael@0: return rv; michael@0: michael@0: #elif defined(XP_UNIX) michael@0: char fullPath[MAXPATHLEN]; michael@0: michael@0: if (!realpath(aPath, fullPath)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_NewNativeLocalFile(nsDependentCString(fullPath), true, michael@0: aResult); michael@0: #elif defined(XP_WIN) michael@0: WCHAR fullPath[MAXPATHLEN]; michael@0: michael@0: if (!_wfullpath(fullPath, NS_ConvertUTF8toUTF16(aPath).get(), MAXPATHLEN)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_NewLocalFile(nsDependentString(fullPath), true, michael@0: aResult); michael@0: michael@0: #else michael@0: #error Platform-specific logic needed here. michael@0: #endif michael@0: }