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