gecko-dev/toolkit/profile/nsToolkitProfileService.cpp

1118 lines
30 KiB
C++

/* -*- 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/Snprintf.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
}