pjs/profile/src/nsProfile.cpp

2635 строки
86 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nscore.h"
#include "nsProfile.h"
#ifdef MOZ_PROFILELOCKING
#include "nsProfileLock.h"
#endif
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "pratom.h"
#include "prmem.h"
#include "plstr.h"
#include "prenv.h"
#include "nsIFactory.h"
#include "nsIComponentManager.h"
#include "nsIEnumerator.h"
#include "nsXPIDLString.h"
#include "nsEscape.h"
#include "nsIURL.h"
#include "nsNativeCharsetUtils.h"
#include "prprf.h"
#include "nsIIOService.h"
#include "nsNetUtil.h"
#include "nsPrefMigration.h"
#include "nsIPrefMigration.h"
#include "nsPrefMigrationCIDs.h"
#include "nsFileStream.h"
#include "nsIPromptService.h"
#include "nsIStreamListener.h"
#include "nsIServiceManager.h"
#include "nsCOMPtr.h"
#include "nsIModule.h"
#include "nsIGenericFactory.h"
#include "nsICookieService.h"
#include "nsICategoryManager.h"
#include "nsXPCOM.h"
#include "nsISupportsPrimitives.h"
#include "nsIDirectoryService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIChromeRegistrySea.h"
#include "nsIStringBundle.h"
#include "nsIObserverService.h"
#include "nsHashtable.h"
#include "nsIAtom.h"
#include "nsProfileDirServiceProvider.h"
#include "nsISessionRoaming.h"
// Interfaces Needed
#include "nsIDocShell.h"
#include "nsIWebBrowserChrome.h"
#include "nsIBaseWindow.h"
#include "nsIDialogParamBlock.h"
#include "nsIDOMWindowInternal.h"
#include "nsIWindowWatcher.h"
#include "jsapi.h"
#include "nsIJSContextStack.h"
#include "nsEmbedCID.h"
#if defined(XP_MAC) || defined(XP_MACOSX)
#define OLD_REGISTRY_FILE_NAME "Netscape Registry"
#elif defined(XP_WIN) || defined(XP_OS2)
#define OLD_REGISTRY_FILE_NAME "nsreg.dat"
#endif
// A default profile name, in case automigration 4x profile fails
#define DEFAULT_PROFILE_NAME (NS_LITERAL_STRING("default").get())
#define PROFILE_SELECTION_URL "chrome://communicator/content/profile/profileSelection.xul"
#define PROFILE_SELECTION_CMD_LINE_ARG "-SelectProfile"
#define PROFILE_MANAGER_URL "chrome://communicator/content/profile/profileSelection.xul?manage=true"
#define PROFILE_MANAGER_CMD_LINE_ARG "-ProfileManager"
#define PROFILE_WIZARD_URL "chrome://communicator/content/profile/createProfileWizard.xul"
#define PROFILE_WIZARD_CMD_LINE_ARG "-ProfileWizard"
#define INSTALLER_CMD_LINE_ARG "-installer"
#define CREATE_PROFILE_CMD_LINE_ARG "-CreateProfile"
#define PROFILE_CMD_LINE_ARG "-P"
#define UILOCALE_CMD_LINE_ARG "-UILocale"
#define CONTENTLOCALE_CMD_LINE_ARG "-contentLocale"
#define PREF_CONFIRM_AUTOMIGRATION "profile.confirm_automigration"
#define PREF_AUTOMIGRATION "profile.allow_automigration"
#define PREF_MIGRATE_ALL "profile.migrate_all"
#define PREF_MIGRATION_BEHAVIOR "profile.migration_behavior"
#define PREF_MIGRATION_DIRECTORY "profile.migration_directory"
#if defined (XP_MAC)
#define CHROME_STYLE nsIWebBrowserChrome::CHROME_WINDOW_BORDERS | nsIWebBrowserChrome::CHROME_WINDOW_CLOSE | nsIWebBrowserChrome::CHROME_CENTER_SCREEN
#else /* the rest */
#define CHROME_STYLE nsIWebBrowserChrome::CHROME_ALL | nsIWebBrowserChrome::CHROME_CENTER_SCREEN
#endif
const char* kDefaultOpenWindowParams = "centerscreen,chrome,modal,titlebar";
const char* kBrandBundleURL = "chrome://branding/locale/brand.properties";
const char* kMigrationBundleURL = "chrome://communicator/locale/profile/migration.properties";
// we want everyone to have the debugging info to the console for now
// to help track down profile manager problems
// when we ship, we'll turn this off
#undef DEBUG_profile_verbose
#ifdef DEBUG_seth
#define DEBUG_profile_verbose 1
#endif
// ProfileAccess varaible (gProfileDataAccess) to access registry operations
// gDataAccessInstCount is used to keep track of instance count to activate
// destructor at the right time (count == 0)
static nsProfileAccess* gProfileDataAccess = nsnull;
static PRInt32 gInstanceCount = 0;
// Profile database to remember which profile has been
// created with UILocale and contentLocale on profileManager
static nsHashtable *gLocaleProfiles = nsnull;
static nsProfileDirServiceProvider *gDirServiceProvider = nsnull;
// IID and CIDs of all the services needed
static NS_DEFINE_CID(kPrefMigrationCID, NS_PREFMIGRATION_CID);
static NS_DEFINE_CID(kPrefConverterCID, NS_PREFCONVERTER_CID);
#define NS_SESSIONROAMING_CONTRACTID "@mozilla.org/profile/session-roaming;1"
/*
Copies the contents of srcDir into destDir.
destDir will be created if it doesn't exist.
*/
static
nsresult RecursiveCopy(nsIFile* srcDir, nsIFile* destDir)
{
nsresult rv;
PRBool isDir;
rv = srcDir->IsDirectory(&isDir);
if (NS_FAILED(rv)) return rv;
if (!isDir) return NS_ERROR_INVALID_ARG;
PRBool exists;
rv = destDir->Exists(&exists);
if (NS_SUCCEEDED(rv) && !exists)
rv = destDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
if (NS_FAILED(rv)) return rv;
PRBool hasMore = PR_FALSE;
nsCOMPtr<nsISimpleEnumerator> dirIterator;
rv = srcDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
if (NS_FAILED(rv)) return rv;
rv = dirIterator->HasMoreElements(&hasMore);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIFile> dirEntry;
while (hasMore)
{
rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(dirEntry));
if (NS_SUCCEEDED(rv))
{
rv = dirEntry->IsDirectory(&isDir);
if (NS_SUCCEEDED(rv))
{
if (isDir)
{
nsCOMPtr<nsIFile> destClone;
rv = destDir->Clone(getter_AddRefs(destClone));
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsILocalFile> newChild(do_QueryInterface(destClone));
nsCAutoString leafName;
dirEntry->GetNativeLeafName(leafName);
newChild->AppendRelativeNativePath(leafName);
rv = RecursiveCopy(dirEntry, newChild);
}
}
else
rv = dirEntry->CopyToNative(destDir, EmptyCString());
}
}
rv = dirIterator->HasMoreElements(&hasMore);
if (NS_FAILED(rv)) return rv;
}
return rv;
}
/*
* Constructor/Destructor
*/
nsProfile::nsProfile()
{
mStartingUp = PR_FALSE;
mAutomigrate = PR_FALSE;
mOutofDiskSpace = PR_FALSE;
mDiskSpaceErrorQuitCalled = PR_FALSE;
mCurrentProfileAvailable = PR_FALSE;
mIsUILocaleSpecified = PR_FALSE;
mIsContentLocaleSpecified = PR_FALSE;
mShutdownProfileToreDownNetwork = PR_FALSE;
mProfileChangeVetoed = PR_FALSE;
mProfileChangeFailed = PR_FALSE;
}
nsProfile::~nsProfile()
{
#if defined(DEBUG_profile_verbose)
printf("~nsProfile \n");
#endif
if (--gInstanceCount == 0) {
delete gProfileDataAccess;
delete gLocaleProfiles;
NS_IF_RELEASE(gDirServiceProvider);
}
}
nsresult
nsProfile::Init()
{
nsresult rv = NS_OK;
if (gInstanceCount++ == 0) {
gProfileDataAccess = new nsProfileAccess;
if (!gProfileDataAccess)
return NS_ERROR_OUT_OF_MEMORY;
gLocaleProfiles = new nsHashtable();
if (!gLocaleProfiles)
return NS_ERROR_OUT_OF_MEMORY;
rv = NS_NewProfileDirServiceProvider(PR_FALSE, &gDirServiceProvider);
if (NS_SUCCEEDED(rv))
rv = gDirServiceProvider->Register();
}
return rv;
}
/*
* nsISupports Implementation
*/
NS_IMPL_THREADSAFE_ADDREF(nsProfile)
NS_IMPL_THREADSAFE_RELEASE(nsProfile)
NS_INTERFACE_MAP_BEGIN(nsProfile)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProfile)
NS_INTERFACE_MAP_ENTRY(nsIProfile)
NS_INTERFACE_MAP_ENTRY(nsIProfileInternal)
NS_INTERFACE_MAP_ENTRY(nsIProfileChangeStatus)
NS_INTERFACE_MAP_END
/*
* nsIProfile Implementation
*/
NS_IMETHODIMP
nsProfile::GetAutomigrate(PRBool *aAutomigrate)
{
NS_ENSURE_ARG_POINTER(aAutomigrate);
*aAutomigrate = mAutomigrate;
return NS_OK;
}
NS_IMETHODIMP
nsProfile::SetAutomigrate(PRBool aAutomigrate)
{
mAutomigrate = aAutomigrate;
return NS_OK;
}
NS_IMETHODIMP
nsProfile::StartupWithArgs(nsICmdLineService *cmdLineArgs, PRBool canInteract)
{
nsresult rv;
struct ScopeFlag
{
ScopeFlag(PRBool *flagPtr) : mFlagPtr(flagPtr)
{ *mFlagPtr = PR_TRUE; }
~ScopeFlag()
{ *mFlagPtr = PR_FALSE; }
PRBool *mFlagPtr;
};
// initializations for profile manager
PRBool profileDirSet = PR_FALSE;
nsCString profileURLStr("");
#ifdef DEBUG_profile_verbose
printf("Profile Manager : Profile Wizard and Manager activites : Begin\n");
#endif
ScopeFlag startupFlag(&mStartingUp);
if (cmdLineArgs)
rv = ProcessArgs(cmdLineArgs, canInteract, &profileDirSet, profileURLStr);
// This boolean is set only when an automigrated user runs out of disk space
// and chooses to cancel further operations from the dialogs presented...
if (mDiskSpaceErrorQuitCalled)
return NS_ERROR_FAILURE;
if (!profileDirSet) {
rv = LoadDefaultProfileDir(profileURLStr, canInteract);
if (NS_FAILED(rv)) return rv;
}
// Ensure that by this point we have a current profile.
// If -CreateProfile was used, we won't, and we're supposed to exit.
nsXPIDLString currentProfileStr;
rv = GetCurrentProfile(getter_Copies(currentProfileStr));
if (NS_FAILED(rv) || (*(const PRUnichar*)currentProfileStr == 0)) {
return NS_ERROR_ABORT;
}
// check UILocale is specified on profileManager, otherwise,
// when -UILocale option is specified, install the UILocale
// -UILocale or -contentLocale is not specified
if (mIsUILocaleSpecified == PR_FALSE && mIsContentLocaleSpecified == PR_FALSE) {
return NS_OK;
}
nsCOMPtr<nsIFile> profileDir;
rv = GetCurrentProfileDir(getter_AddRefs(profileDir));
if (NS_FAILED(rv)) return rv;
nsCAutoString pathBuf;
rv = profileDir->GetNativePath(pathBuf);
if (NS_FAILED(rv)) return rv;
// -UILocale or -contentLocale has been specified, but
// user has selected UILocale and contentLocale for this profile
// on profileManager
// We should not set here
nsCStringKey key(pathBuf);
if (NS_PTR_TO_INT32(gLocaleProfiles->Get(&key)) == PR_TRUE) {
#ifdef DEBUG_profile_verbose
printf(" already set UILocale and contentLocale: %s\n", pathBuf.get());
printf(" will not install locale\n");
#endif
return NS_OK;
}
gLocaleProfiles->Remove(&key);
nsCOMPtr<nsIChromeRegistrySea> chromeRegistry =
do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
// Install to the profile
nsCAutoString fileStr;
rv = NS_GetURLSpecFromFile(profileDir, fileStr);
if (NS_FAILED(rv)) return rv;
// NEED TO FIX: when current UILocale and contentLocale are same with specified locales,
// we shouldn't install them again here. But packageRegistry->GetSelectedLocale() doesn't
// work here properly. It always returns global and global-region of default not current
// profile
if (!mUILocaleName.IsEmpty()) {
#ifdef DEBUG_profile_verbose
printf(" install new UILocaleName: %s\n", mUILocaleName.get());
#endif
rv = chromeRegistry->SelectLocaleForProfile(mUILocaleName,
NS_ConvertUTF8toUTF16(fileStr).get());
if (NS_FAILED(rv)) return rv;
}
if (!mContentLocaleName.IsEmpty()) {
#ifdef DEBUG_profile_verbose
printf(" install new mContentLocaleName: %s\n", mContentLocaleName.get());
#endif
rv = chromeRegistry->SelectLocaleForProfile(mContentLocaleName,
NS_ConvertUTF8toUTF16(fileStr).get());
if (NS_FAILED(rv)) return rv;
}
#ifdef DEBUG_profile_verbose
printf("Profile Manager : Profile Wizard and Manager activites : End\n");
#endif
return NS_OK;
}
NS_IMETHODIMP
nsProfile::GetIsStartingUp(PRBool *aIsStartingUp)
{
NS_ENSURE_ARG_POINTER(aIsStartingUp);
*aIsStartingUp = mStartingUp;
return NS_OK;
}
nsresult
nsProfile::LoadDefaultProfileDir(nsCString & profileURLStr, PRBool canInteract)
{
nsresult rv;
nsCOMPtr<nsIURI> profileURL;
PRInt32 numProfiles=0;
GetProfileCount(&numProfiles);
if (profileURLStr.IsEmpty())
{
nsCOMPtr<nsIPrefBranch> prefBranch;
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = prefs->GetBranch(nsnull, getter_AddRefs(prefBranch));
if (NS_FAILED(rv)) return rv;
// If this flag is TRUE, it makes the multiple profile case
// just like the single profile case - the profile will be
// set to that returned by GetCurrentProfile(). It will prevent
// the profile selection dialog from being shown when we have
// multiple profiles.
PRBool startWithLastUsedProfile = PR_FALSE;
// But first, make sure this app supports this.
PRBool cantAutoSelect;
rv = prefBranch->GetBoolPref("profile.manage_only_at_launch", &cantAutoSelect);
if (NS_SUCCEEDED(rv) && !cantAutoSelect)
GetStartWithLastUsedProfile(&startWithLastUsedProfile);
// This means that there was no command-line argument to force
// profile UI to come up. But we need the UI anyway if there
// are no profiles yet, or if there is more than one.
if (numProfiles == 0)
{
rv = CreateDefaultProfile();
if (NS_FAILED(rv)) return rv;
// Will get set in call to SetCurrentProfile() below
}
else if (numProfiles == 1 || startWithLastUsedProfile)
{
// If we get here and the 1 profile is the current profile,
// which can happen with QuickLaunch, there's no need to do
// any futher work.
if (mCurrentProfileAvailable)
return NS_OK;
// Make sure the profile dir exists. If not, we need the UI
nsCOMPtr<nsIFile> curProfileDir;
PRBool exists = PR_FALSE;
rv = GetCurrentProfileDir(getter_AddRefs(curProfileDir));
if (NS_SUCCEEDED(rv))
rv = curProfileDir->Exists(&exists);
if (NS_FAILED(rv) || !exists)
profileURLStr = PROFILE_MANAGER_URL;
if (exists)
{
#ifdef MOZ_PROFILELOCKING
// If the profile is locked, we need the UI
nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(curProfileDir));
nsProfileLock tempLock;
rv = tempLock.Lock(localFile, nsnull);
if (NS_FAILED(rv))
profileURLStr = PROFILE_MANAGER_URL;
#endif
}
}
else
profileURLStr = PROFILE_SELECTION_URL;
}
if (!profileURLStr.IsEmpty())
{
if (!canInteract) return NS_ERROR_PROFILE_REQUIRES_INTERACTION;
nsCOMPtr<nsIWindowWatcher> windowWatcher(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDialogParamBlock> ioParamBlock(do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID, &rv));
if (NS_FAILED(rv)) return rv;
// String0 -> mode
// Int0 <- result code (1 == OK, 0 == Cancel)
ioParamBlock->SetNumberStrings(1);
ioParamBlock->SetString(0, NS_LITERAL_STRING("startup").get());
nsCOMPtr<nsIDOMWindow> newWindow;
rv = windowWatcher->OpenWindow(nsnull,
profileURLStr.get(),
"_blank",
kDefaultOpenWindowParams,
ioParamBlock,
getter_AddRefs(newWindow));
if (NS_FAILED(rv)) return rv;
PRInt32 dialogConfirmed;
ioParamBlock->GetInt(0, &dialogConfirmed);
if (dialogConfirmed == 0) return NS_ERROR_ABORT;
}
nsXPIDLString currentProfileStr;
rv = GetCurrentProfile(getter_Copies(currentProfileStr));
if (NS_FAILED(rv)) return rv;
// if at this point we have a current profile but it is not set, set it
if (!mCurrentProfileAvailable) {
rv = SetCurrentProfile(currentProfileStr);
if (NS_FAILED(rv)) return rv;
}
return NS_OK;
}
nsresult
nsProfile::ConfirmAutoMigration(PRBool canInteract, PRBool *confirmed)
{
NS_ENSURE_ARG_POINTER(confirmed);
nsCOMPtr<nsIPrefBranch> prefBranch;
*confirmed = PR_FALSE;
nsresult rv;
// First check PREF_CONFIRM_AUTOMIGRATION.
// If FALSE, we go ahead and migrate without asking.
PRBool confirmAutomigration = PR_TRUE;
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = prefs->GetBranch(nsnull, getter_AddRefs(prefBranch));
if (NS_FAILED(rv)) return rv;
(void)prefBranch->GetBoolPref(PREF_CONFIRM_AUTOMIGRATION, &confirmAutomigration);
if (!confirmAutomigration) {
*confirmed = PR_TRUE;
return NS_OK;
}
// If allowed, put up a confirm dialog and ask the user
if (!canInteract)
return NS_ERROR_PROFILE_REQUIRES_INTERACTION;
nsCOMPtr<nsIStringBundleService> stringBundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIStringBundle> migrationBundle, brandBundle;
rv = stringBundleService->CreateBundle(kMigrationBundleURL, getter_AddRefs(migrationBundle));
if (NS_FAILED(rv)) return rv;
rv = stringBundleService->CreateBundle(kBrandBundleURL, getter_AddRefs(brandBundle));
if (NS_FAILED(rv)) return rv;
nsXPIDLString brandName;
rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(), getter_Copies(brandName));
if (NS_FAILED(rv)) return rv;
nsXPIDLString msgString, dialogTitle, button0Title, button1Title;
const PRUnichar *formatStrings[] = { brandName.get(), brandName.get() };
rv = migrationBundle->FormatStringFromName(NS_LITERAL_STRING("confirmMigration").get(),
formatStrings, 2, getter_Copies(msgString));
if (NS_FAILED(rv)) return rv;
rv = migrationBundle->GetStringFromName(NS_LITERAL_STRING("dialogTitle").get(), getter_Copies(dialogTitle));
if (NS_FAILED(rv)) return rv;
rv = migrationBundle->GetStringFromName(NS_LITERAL_STRING("migrate").get(), getter_Copies(button0Title));
if (NS_FAILED(rv)) return rv;
rv = migrationBundle->GetStringFromName(NS_LITERAL_STRING("manage").get(), getter_Copies(button1Title));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIPromptService> promptService(do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) return rv;
PRInt32 buttonPressed;
rv = promptService->ConfirmEx(nsnull, dialogTitle.get(), msgString.get(),
(nsIPromptService::BUTTON_POS_0 * nsIPromptService::BUTTON_TITLE_IS_STRING) +
(nsIPromptService::BUTTON_POS_1 * nsIPromptService::BUTTON_TITLE_IS_STRING),
button0Title, button1Title, nsnull,
nsnull, nsnull, &buttonPressed);
if (NS_FAILED(rv)) return rv;
*confirmed = (buttonPressed == 0);
return NS_OK;
}
nsresult
nsProfile::AutoMigrate()
{
nsresult rv = NS_OK;
// automatically migrate the one 4.x profile
rv = MigrateAllProfiles();
// Create a default profile if automigration failed for reasons
// other than out of disk space case...
if (NS_FAILED(rv) && !mOutofDiskSpace)
{
#ifdef DEBUG_profile
printf("AutoMigration failed. Let's create a default 5.0 profile.\n");
#endif
rv = CreateDefaultProfile();
if (NS_FAILED(rv)) return rv;
}
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
gProfileDataAccess->UpdateRegistry(nsnull);
return rv;
}
nsresult
nsProfile::ProcessArgs(nsICmdLineService *cmdLineArgs,
PRBool canInteract,
PRBool* profileDirSet,
nsCString & profileURLStr)
{
NS_ASSERTION(cmdLineArgs, "Invalid cmdLineArgs");
NS_ASSERTION(profileDirSet, "Invalid profileDirSet");
nsresult rv;
nsXPIDLCString cmdResult;
nsCOMPtr<nsILocalFile> currProfileDir;
// keep track of if the user passed us any profile related command line args
// if they did, we won't force migration
PRBool foundProfileCommandArg = PR_FALSE;
#ifdef DEBUG_profile_verbose
printf("Profile Manager : Command Line Options : Begin\n");
#endif
// check for command line arguments for profile manager
// -UILocale command
rv = cmdLineArgs->GetCmdLineValue(UILOCALE_CMD_LINE_ARG, getter_Copies(cmdResult));
if (NS_SUCCEEDED(rv))
{
if (cmdResult) {
mIsUILocaleSpecified = PR_TRUE;
mUILocaleName = cmdResult;
}
}
// -contentLocale command
rv = cmdLineArgs->GetCmdLineValue(CONTENTLOCALE_CMD_LINE_ARG, getter_Copies(cmdResult));
if (NS_SUCCEEDED(rv))
{
if (cmdResult) {
mIsContentLocaleSpecified = PR_TRUE;
mContentLocaleName = cmdResult;
}
}
// -P command line option works this way:
// apprunner -P profilename
// runs the app using the profile <profilename>
// remembers profile for next time
rv = cmdLineArgs->GetCmdLineValue(PROFILE_CMD_LINE_ARG, getter_Copies(cmdResult));
if (NS_SUCCEEDED(rv))
{
if (cmdResult) {
foundProfileCommandArg = PR_TRUE;
nsAutoString currProfileName;
rv = NS_CopyNativeToUnicode(cmdResult, currProfileName);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to convert ProfileName to unicode");
#ifdef DEBUG_profile
printf("ProfileName : %s\n", (const char*)cmdResult);
#endif /* DEBUG_profile */
PRBool exists;
rv = ProfileExists(currProfileName.get(), &exists);
if (NS_FAILED(rv)) return rv;
if (!exists) {
PRInt32 num5xProfiles = 0;
PRInt32 num4xProfiles = 0;
GetProfileCount(&num5xProfiles);
Get4xProfileCount(&num4xProfiles);
if (num5xProfiles == 0 && num4xProfiles == 0) {
profileURLStr = PROFILE_WIZARD_URL;
}
else if (num5xProfiles > 0) {
profileURLStr = PROFILE_SELECTION_URL;
}
else if (num4xProfiles > 0) {
profileURLStr = PROFILE_MANAGER_URL;
}
*profileDirSet = PR_FALSE;
}
else {
rv = SetCurrentProfile(currProfileName.get());
if (NS_SUCCEEDED(rv))
*profileDirSet = PR_TRUE;
}
}
}
// -CreateProfile command line option works this way:
// apprunner -CreateProfile profilename
// creates a new profile named <profilename> and sets the directory to your CWD
// runs app using that profile
// remembers profile for next time
// - OR -
// apprunner -CreateProfile "profilename profiledir"
// creates a new profile named <profilename> and sets the directory to <profiledir>
// runs app using that profile
// remembers profile for next time
rv = cmdLineArgs->GetCmdLineValue(CREATE_PROFILE_CMD_LINE_ARG, getter_Copies(cmdResult));
if (NS_SUCCEEDED(rv))
{
if (cmdResult) {
#ifdef DEBUG_profile_verbose
printf("profileName & profileDir are: %s\n", (const char*)cmdResult);
#endif
foundProfileCommandArg = PR_TRUE;
nsAutoString currProfileName;
char *tmpStr;
rv = NS_CopyNativeToUnicode(
nsDependentCString(nsCRT::strtok(cmdResult.BeginWriting(), " ", &tmpStr)),
currProfileName);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to convert ProfileName to unicode");
char *currProfileDirString = nsCRT::strtok(tmpStr, " ", &tmpStr);
if (currProfileDirString && *currProfileDirString) {
rv = NS_NewNativeLocalFile(nsDependentCString(currProfileDirString),
PR_TRUE, getter_AddRefs(currProfileDir));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
}
else {
// No directory name provided. Place it in
// NS_APP_USER_PROFILES_ROOT_DIR
nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) return rv;
rv = directoryService->Get(NS_APP_USER_PROFILES_ROOT_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(currProfileDir));
if (NS_FAILED(rv)) return rv;
}
nsAutoString currProfilePath;
currProfileDir->GetPath(currProfilePath);
rv = CreateNewProfile(currProfileName.get(), currProfilePath.get(), nsnull, PR_TRUE);
if (NS_SUCCEEDED(rv)) {
*profileDirSet = PR_TRUE;
mCurrentProfileAvailable = PR_TRUE;
gProfileDataAccess->SetCurrentProfile(currProfileName.get());
// Load new profile prefs for the sake of tinderbox scripts
// which assume prefs.js exists after -CreateProfile.
nsCOMPtr<nsIFile> newProfileDir;
GetProfileDir(currProfileName.get(), getter_AddRefs(newProfileDir));
if (newProfileDir) {
nsCOMPtr<nsIFile> localDir;
GetLocalProfileDir(currProfileName.get(), getter_AddRefs(localDir));
gDirServiceProvider->SetProfileDir(newProfileDir, localDir);
}
rv = LoadNewProfilePrefs();
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
gProfileDataAccess->UpdateRegistry(nsnull);
}
rv = ForgetCurrentProfile();
if (NS_FAILED(rv)) return rv;
}
}
// Start Profile Manager
rv = cmdLineArgs->GetCmdLineValue(PROFILE_MANAGER_CMD_LINE_ARG, getter_Copies(cmdResult));
if (NS_SUCCEEDED(rv))
{
if (cmdResult) {
foundProfileCommandArg = PR_TRUE;
profileURLStr = PROFILE_MANAGER_URL;
}
}
// Start Profile Selection
rv = cmdLineArgs->GetCmdLineValue(PROFILE_SELECTION_CMD_LINE_ARG, getter_Copies(cmdResult));
if (NS_SUCCEEDED(rv))
{
if (cmdResult) {
foundProfileCommandArg = PR_TRUE;
profileURLStr = PROFILE_SELECTION_URL;
}
}
// Start Profile Wizard
rv = cmdLineArgs->GetCmdLineValue(PROFILE_WIZARD_CMD_LINE_ARG, getter_Copies(cmdResult));
if (NS_SUCCEEDED(rv))
{
if (cmdResult) {
foundProfileCommandArg = PR_TRUE;
profileURLStr = PROFILE_WIZARD_URL;
}
}
PRBool forceMigration = PR_FALSE;
if (!foundProfileCommandArg) {
rv = gProfileDataAccess->DetermineForceMigration(&forceMigration);
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to determine if we should force migration");
}
nsCOMPtr<nsIPrefBranch> prefBranch;
// First check PREF_AUTOMIGRATION.
PRBool allowAutoMigration = PR_TRUE;
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = prefs->GetBranch(nsnull, getter_AddRefs(prefBranch));
if (NS_FAILED(rv)) return rv;
(void)prefBranch->GetBoolPref(PREF_AUTOMIGRATION, &allowAutoMigration);
// Start Migaration activity
rv = cmdLineArgs->GetCmdLineValue(INSTALLER_CMD_LINE_ARG, getter_Copies(cmdResult));
if (allowAutoMigration && (NS_SUCCEEDED(rv) || forceMigration))
{
if (cmdResult || forceMigration) {
PRBool migrateAll = PR_FALSE;
(void)prefBranch->GetBoolPref(PREF_MIGRATE_ALL, &migrateAll);
rv = MigrateProfileInfo();
if (NS_FAILED(rv)) return rv;
PRInt32 num4xProfiles = 0;
rv = Get4xProfileCount(&num4xProfiles);
if (NS_FAILED(rv)) return rv;
PRInt32 numProfiles = 0;
GetProfileCount(&numProfiles);
if (num4xProfiles == 0 && numProfiles == 0) {
// Let us create a default 5.0 profile
CreateDefaultProfile();
if (NS_FAILED(rv)) return rv;
}
else if (num4xProfiles == 0 && numProfiles == 1) {
profileURLStr = "";
}
else if ((num4xProfiles == 1 || migrateAll) && numProfiles == 0) {
PRBool confirmed = PR_FALSE;
if (NS_SUCCEEDED(ConfirmAutoMigration(canInteract, &confirmed)) && confirmed)
AutoMigrate();
else
profileURLStr = PROFILE_MANAGER_URL;
}
else if (numProfiles > 1)
{
profileURLStr = PROFILE_SELECTION_URL;
}
else {
// show the profile manager
profileURLStr = PROFILE_MANAGER_URL;
}
}
}
#ifdef DEBUG_profile_verbose
printf("Profile Manager : Command Line Options : End\n");
#endif
return NS_OK;
}
/*
* Getters
*/
// Gets the profiles directory for a given profile
// Sets the given profile to be a current profile
NS_IMETHODIMP nsProfile::GetProfileDir(const PRUnichar *profileName, nsIFile **profileDir)
{
NS_ENSURE_ARG(profileName);
NS_ENSURE_ARG_POINTER(profileDir);
*profileDir = nsnull;
nsresult rv = NS_OK;
#if defined(DEBUG_profile_verbose)
printf("ProfileManager : GetProfileDir\n");
#endif
ProfileStruct *aProfile;
rv = gProfileDataAccess->GetValue(profileName, &aProfile);
if (NS_FAILED(rv)) return rv;
if (aProfile == nsnull)
return NS_ERROR_FAILURE;
nsCOMPtr<nsILocalFile>aProfileDir;
rv = aProfile->GetResolvedProfileDir(getter_AddRefs(aProfileDir));
if (NS_SUCCEEDED(rv) && aProfileDir)
{
#ifdef XP_MAC
PRBool exists;
rv = aProfileDir->Exists(&exists);
if (NS_FAILED(rv)) return rv;
if (exists) {
PRBool inTrash;
nsCOMPtr<nsIFile> trashFolder;
rv = NS_GetSpecialDirectory(NS_MAC_TRASH_DIR, getter_AddRefs(trashFolder));
if (NS_FAILED(rv)) return rv;
rv = trashFolder->Contains(aProfileDir, PR_TRUE, &inTrash);
if (NS_FAILED(rv)) return rv;
if (inTrash) {
aProfileDir = nsnull;
rv = NS_ERROR_FILE_NOT_FOUND;
}
}
#endif
*profileDir = aProfileDir;
NS_IF_ADDREF(*profileDir);
}
delete aProfile;
return rv;
}
NS_IMETHODIMP nsProfile::GetProfilePath(const PRUnichar *profileName, PRUnichar **_retval)
{
NS_ENSURE_ARG(profileName);
NS_ENSURE_ARG_POINTER(_retval);
*_retval = nsnull;
nsCOMPtr<nsIFile> profileDir;
nsresult rv = GetProfileDir(profileName, getter_AddRefs(profileDir));
if (NS_FAILED(rv)) return rv;
PRBool isSalted;
nsCOMPtr<nsIFile> prettyDir(profileDir);
rv = IsProfileDirSalted(profileDir, &isSalted);
if (NS_SUCCEEDED(rv) && isSalted) {
nsCOMPtr<nsIFile> parentDir;
rv = profileDir->GetParent(getter_AddRefs(parentDir));
if (NS_SUCCEEDED(rv))
prettyDir = parentDir;
}
nsAutoString path;
rv = prettyDir->GetPath(path);
if (NS_FAILED(rv)) return rv;
*_retval = ToNewUnicode(path);
return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP nsProfile::GetOriginalProfileDir(const PRUnichar *profileName, nsILocalFile **originalDir)
{
NS_ENSURE_ARG(profileName);
NS_ENSURE_ARG_POINTER(originalDir);
*originalDir = nsnull;
Update4xProfileInfo();
return gProfileDataAccess->GetOriginalProfileDir(profileName, originalDir);
}
NS_IMETHODIMP nsProfile::GetProfileLastModTime(const PRUnichar *profileName, PRInt64 *_retval)
{
NS_ENSURE_ARG(profileName);
NS_ENSURE_ARG_POINTER(_retval);
nsresult rv;
// First, see if we can get the lastModTime from the registry.
// We only started putting it there from mozilla1.0.1
// The mod time will be zero if it has not been set.
ProfileStruct *profileInfo = nsnull;
rv = gProfileDataAccess->GetValue(profileName, &profileInfo);
if (NS_SUCCEEDED(rv)) {
PRInt64 lastModTime = profileInfo->lastModTime;
delete profileInfo;
if (!LL_IS_ZERO(lastModTime)) {
*_retval = lastModTime;
return NS_OK;
}
}
// Since we couldn't get a valid mod time from the registry,
// check the date of prefs.js. Since August, 2000 it is always
// written out on quitting the application.
nsCOMPtr<nsIFile> profileDir;
rv = GetProfileDir(profileName, getter_AddRefs(profileDir));
if (NS_FAILED(rv))
return rv;
rv = profileDir->AppendNative(NS_LITERAL_CSTRING("prefs.js"));
if (NS_FAILED(rv))
return rv;
return profileDir->GetLastModifiedTime(_retval);
}
NS_IMETHODIMP nsProfile::GetDefaultProfileParentDir(nsIFile **aDefaultProfileParentDir)
{
NS_ENSURE_ARG_POINTER(aDefaultProfileParentDir);
nsresult rv;
nsCOMPtr<nsIFile> aDir;
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILES_ROOT_DIR, getter_AddRefs(aDir));
NS_ENSURE_SUCCESS(rv, rv);
*aDefaultProfileParentDir = aDir;
NS_ADDREF(*aDefaultProfileParentDir);
return NS_OK;
}
// Gets the number of profiles
// Location: Common/Profiles
NS_IMETHODIMP nsProfile::GetProfileCount(PRInt32 *numProfiles)
{
NS_ENSURE_ARG_POINTER(numProfiles);
*numProfiles = 0;
gProfileDataAccess->GetNumProfiles(numProfiles);
return NS_OK;
}
// If only a single profile exists
// return the name of the single profile.
// Otherwise it return the name of the first valid profile.
NS_IMETHODIMP nsProfile::GetFirstProfile(PRUnichar **profileName)
{
NS_ENSURE_ARG_POINTER(profileName);
gProfileDataAccess->GetFirstProfile(profileName);
gProfileDataAccess->SetCurrentProfile(*profileName);
return NS_OK;
}
NS_IMETHODIMP nsProfile::GetStartWithLastUsedProfile(PRBool *aStartWithLastUsedProfile)
{
NS_ENSURE_ARG_POINTER(aStartWithLastUsedProfile);
return gProfileDataAccess->GetStartWithLastUsedProfile(aStartWithLastUsedProfile);
}
NS_IMETHODIMP nsProfile::SetStartWithLastUsedProfile(PRBool aStartWithLastUsedProfile)
{
return gProfileDataAccess->SetStartWithLastUsedProfile(aStartWithLastUsedProfile);
}
// Returns the name of the current profile i.e., the last used profile
NS_IMETHODIMP
nsProfile::GetCurrentProfile(PRUnichar **profileName)
{
NS_ENSURE_ARG_POINTER(profileName);
*profileName = nsnull;
if (!mCurrentProfileName.IsEmpty())
*profileName = ToNewUnicode(mCurrentProfileName);
else
gProfileDataAccess->GetCurrentProfile(profileName);
return (*profileName == nsnull) ? NS_ERROR_FAILURE : NS_OK;
}
NS_IMETHODIMP
nsProfile::SetCurrentProfile(const PRUnichar * aCurrentProfile)
{
NS_ENSURE_ARG(aCurrentProfile);
nsresult rv;
nsCOMPtr<nsIFile> profileDir;
PRBool exists;
// Ensure that the profile exists and its directory too.
rv = GetProfileDir(aCurrentProfile, getter_AddRefs(profileDir));
if (NS_FAILED(rv)) return rv;
rv = profileDir->Exists(&exists);
if (NS_FAILED(rv)) return rv;
if (!exists) return NS_ERROR_FILE_NOT_FOUND;
PRBool isSwitch;
if (mCurrentProfileAvailable)
{
nsXPIDLString currProfileName;
rv = GetCurrentProfile(getter_Copies(currProfileName));
if (NS_FAILED(rv)) return rv;
if (nsCRT::strcmp(aCurrentProfile, currProfileName.get()) == 0)
return NS_OK;
else
isSwitch = PR_TRUE;
}
else
isSwitch = PR_FALSE;
#ifdef MOZ_PROFILELOCKING
nsProfileLock localLock;
nsCOMPtr<nsILocalFile> localProfileDir(do_QueryInterface(profileDir, &rv));
if (NS_FAILED(rv)) return rv;
rv = localLock.Lock(localProfileDir, nsnull);
if (NS_FAILED(rv))
{
NS_ERROR("Could not get profile directory lock.");
return rv;
}
#endif
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_TRUE(observerService, NS_ERROR_FAILURE);
nsISupports *subject = (nsISupports *)((nsIProfile *)this);
NS_NAMED_LITERAL_STRING(switchString, "switch");
NS_NAMED_LITERAL_STRING(startupString, "startup");
const nsAFlatString& context = isSwitch ? switchString : startupString;
if (isSwitch)
{
// Phase 1: See if anybody objects to the profile being changed.
mProfileChangeVetoed = PR_FALSE;
observerService->NotifyObservers(subject, "profile-approve-change", context.get());
if (mProfileChangeVetoed)
return NS_OK;
// Phase 2a: Send the network teardown notification
observerService->NotifyObservers(subject, "profile-change-net-teardown", context.get());
mShutdownProfileToreDownNetwork = PR_TRUE;
// Phase 2b: Send the "teardown" notification
observerService->NotifyObservers(subject, "profile-change-teardown", context.get());
if (mProfileChangeVetoed)
{
// Notify we will not proceed with changing the profile
observerService->NotifyObservers(subject, "profile-change-teardown-veto", context.get());
// Bring network back online and return
observerService->NotifyObservers(subject, "profile-change-net-restore", context.get());
return NS_OK;
}
// Phase 2c: Now that things are torn down, force JS GC so that things which depend on
// resources which are about to go away in "profile-before-change" are destroyed first.
nsCOMPtr<nsIThreadJSContextStack> stack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
if (NS_SUCCEEDED(rv))
{
JSContext *cx = nsnull;
stack->GetSafeJSContext(&cx);
if (cx)
::JS_GC(cx);
}
// Phase 3: Notify observers of a profile change
observerService->NotifyObservers(subject, "profile-before-change", context.get());
if (mProfileChangeFailed)
return NS_ERROR_ABORT;
UpdateCurrentProfileModTime(PR_FALSE);
}
#ifdef MOZ_PROFILELOCKING
// Do the profile switch
localLock.Unlock(); // gDirServiceProvider will get and hold its own lock
#endif
nsCOMPtr<nsIFile> localDir;
GetLocalProfileDir(aCurrentProfile, getter_AddRefs(localDir));
gDirServiceProvider->SetProfileDir(profileDir, localDir);
mCurrentProfileName.Assign(aCurrentProfile);
gProfileDataAccess->SetCurrentProfile(aCurrentProfile);
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
gProfileDataAccess->UpdateRegistry(nsnull);
if (NS_FAILED(rv)) return rv;
mCurrentProfileAvailable = PR_TRUE;
if (!isSwitch)
{
// Ensure that the prefs service exists so it can respond to
// the notifications we're about to send around. It needs to.
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "Could not get prefs service");
}
if (mShutdownProfileToreDownNetwork)
{
// Bring network back online
observerService->NotifyObservers(subject, "profile-change-net-restore", context.get());
mShutdownProfileToreDownNetwork = PR_FALSE;
if (mProfileChangeFailed)
return NS_ERROR_ABORT;
}
// Roaming download
// Tolerate errors. Maybe the roaming extension isn't installed.
nsCOMPtr <nsISessionRoaming> roam =
do_GetService(NS_SESSIONROAMING_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
roam->BeginSession();
// Phase 4: Notify observers that the profile has changed - Here they respond to new profile
observerService->NotifyObservers(subject, "profile-do-change", context.get());
if (mProfileChangeFailed)
return NS_ERROR_ABORT;
// Phase 5: Now observers can respond to something another observer did in phase 4
observerService->NotifyObservers(subject, "profile-after-change", context.get());
if (mProfileChangeFailed)
return NS_ERROR_ABORT;
// Now that a profile is established, set the profile defaults dir for the locale of this profile
rv = DefineLocaleDefaultsDir();
NS_ASSERTION(NS_SUCCEEDED(rv), "nsProfile::DefineLocaleDefaultsDir failed");
// Phase 6: One last notification after the new profile is established
observerService->NotifyObservers(subject, "profile-initial-state", context.get());
if (mProfileChangeFailed)
return NS_ERROR_ABORT;
nsCOMPtr<nsICategoryManager> catman =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
if(NS_SUCCEEDED(rv) && catman)
{
nsCOMPtr<nsISimpleEnumerator> enumItem;
rv = catman->EnumerateCategory(NS_PROFILE_STARTUP_CATEGORY, getter_AddRefs(enumItem));
if(NS_SUCCEEDED(rv) && enumItem)
{
while (PR_TRUE)
{
nsCOMPtr<nsISupportsCString> contractid;
rv = enumItem->GetNext(getter_AddRefs(contractid));
if (NS_FAILED(rv) || !contractid) break;
nsCAutoString contractidString;
contractid->GetData(contractidString);
nsCOMPtr <nsIProfileStartupListener> listener = do_GetService(contractidString.get(), &rv);
if (listener)
listener->OnProfileStartup(aCurrentProfile);
}
}
}
nsCOMPtr<nsIPrefBranch> prefBranch;
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = prefs->GetBranch(nsnull, getter_AddRefs(prefBranch));
if (NS_FAILED(rv)) return rv;
PRBool prefs_converted = PR_FALSE;
(void)prefBranch->GetBoolPref("prefs.converted-to-utf8", &prefs_converted);
if (!prefs_converted)
{
nsCOMPtr <nsIPrefConverter> pPrefConverter = do_GetService(kPrefConverterCID, &rv);
if (!pPrefConverter) return NS_ERROR_FAILURE;
rv = pPrefConverter->ConvertPrefsToUTF8();
if (NS_FAILED(rv)) return rv;
}
return NS_OK;
}
// Returns the name of the current profile directory
NS_IMETHODIMP nsProfile::GetCurrentProfileDir(nsIFile **profileDir)
{
NS_ENSURE_ARG_POINTER(profileDir);
nsresult rv;
nsXPIDLString profileName;
rv = GetCurrentProfile(getter_Copies(profileName));
if (NS_FAILED(rv)) return rv;
rv = GetProfileDir(profileName, profileDir);
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
// Performs a "logout" by shutting down the current profile
NS_IMETHODIMP nsProfile::ShutDownCurrentProfile(PRUint32 shutDownType)
{
nsresult rv;
// if shutDownType is not a well know value, skip the notifications
// see DoOnShutdown() in nsAppRunner.cpp for where we use this behaviour to our benefit
if (shutDownType == SHUTDOWN_PERSIST || shutDownType == SHUTDOWN_CLEANSE) {
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_TRUE(observerService, NS_ERROR_FAILURE);
nsISupports *subject = (nsISupports *)((nsIProfile *)this);
NS_NAMED_LITERAL_STRING(cleanseString, "shutdown-cleanse");
NS_NAMED_LITERAL_STRING(persistString, "shutdown-persist");
const nsAFlatString& context = (shutDownType == SHUTDOWN_CLEANSE) ? cleanseString : persistString;
// Phase 1: See if anybody objects to the profile being changed.
mProfileChangeVetoed = PR_FALSE;
observerService->NotifyObservers(subject, "profile-approve-change", context.get());
if (mProfileChangeVetoed)
return NS_OK;
// Phase 2a: Send the network teardown notification
observerService->NotifyObservers(subject, "profile-change-net-teardown", context.get());
mShutdownProfileToreDownNetwork = PR_TRUE;
// Phase 2b: Send the "teardown" notification
observerService->NotifyObservers(subject, "profile-change-teardown", context.get());
// Phase 2c: Now that things are torn down, force JS GC so that things which depend on
// resources which are about to go away in "profile-before-change" are destroyed first.
nsCOMPtr<nsIThreadJSContextStack> stack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
if (NS_SUCCEEDED(rv))
{
JSContext *cx = nsnull;
stack->GetSafeJSContext(&cx);
if (cx)
::JS_GC(cx);
}
// Phase 3: Notify observers of a profile change
observerService->NotifyObservers(subject, "profile-before-change", context.get());
}
// Roaming upload
// Tolerate errors. Maybe the roaming extension isn't installed.
nsCOMPtr <nsISessionRoaming> roam =
do_GetService(NS_SESSIONROAMING_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
roam->EndSession();
gDirServiceProvider->SetProfileDir(nsnull);
UpdateCurrentProfileModTime(PR_TRUE);
mCurrentProfileAvailable = PR_FALSE;
mCurrentProfileName.Truncate(0);
return NS_OK;
}
#define SALT_SIZE 8
#define TABLE_SIZE 36
static const char kSaltExtensionCString[] = ".slt";
#define kSaltExtensionCString_Len PRUint32(sizeof(kSaltExtensionCString)-1)
static const char table[] =
{ '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','0','1','2','3',
'4','5','6','7','8','9'};
// for security, add a level of indirection:
// an extra directory with a hard to guess name.
nsresult
nsProfile::AddLevelOfIndirection(nsIFile *aDir)
{
nsresult rv;
PRBool exists = PR_FALSE;
if (!aDir) return NS_ERROR_NULL_POINTER;
// check if aDir/prefs.js exists, if so, use it.
// else, check if aDir/*.slt exists, if so, use it.
// else, do the salt
nsCOMPtr<nsIFile> prefFile;
rv = aDir->Clone(getter_AddRefs(prefFile));
NS_ENSURE_SUCCESS(rv,rv);
rv = prefFile->AppendNative(NS_LITERAL_CSTRING("prefs.js"));
NS_ENSURE_SUCCESS(rv,rv);
rv = prefFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv,rv);
if (exists) {
// there is a prefs.js file in aDir, so just use aDir and don't salt
return NS_OK;
}
// no prefs.js, now search for a .slt directory
PRBool hasMore = PR_FALSE;
PRBool isDir = PR_FALSE;
nsCOMPtr<nsISimpleEnumerator> dirIterator;
rv = aDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
NS_ENSURE_SUCCESS(rv,rv);
rv = dirIterator->HasMoreElements(&hasMore);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIFile> dirEntry;
while (hasMore) {
rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(dirEntry));
if (NS_SUCCEEDED(rv)) {
rv = dirEntry->IsDirectory(&isDir);
if (NS_SUCCEEDED(rv) && isDir) {
nsCAutoString leafName;
rv = dirEntry->GetNativeLeafName(leafName);
if (NS_SUCCEEDED(rv) && !leafName.IsEmpty()) {
PRUint32 length = leafName.Length();
// check if the filename is the right length, len("xxxxxxxx.slt")
if (length == (SALT_SIZE + kSaltExtensionCString_Len)) {
// check that the filename ends with ".slt"
if (nsCRT::strncmp(leafName.get() + SALT_SIZE,
kSaltExtensionCString,
kSaltExtensionCString_Len) == 0) {
// found a salt directory, use it
rv = aDir->AppendNative(leafName);
return rv;
}
}
}
}
}
rv = dirIterator->HasMoreElements(&hasMore);
NS_ENSURE_SUCCESS(rv,rv);
}
// if we get here, we need to add the extra directory
// turn PR_Now() into milliseconds since epoch
// and salt rand with that.
double fpTime;
LL_L2D(fpTime, PR_Now());
srand((uint)(fpTime * 1e-6 + 0.5)); // use 1e-6, granularity of PR_Now() on the mac is seconds
nsCAutoString saltStr;
PRInt32 i;
for (i=0;i<SALT_SIZE;i++) {
saltStr.Append(table[rand()%TABLE_SIZE]);
}
saltStr.Append(kSaltExtensionCString, kSaltExtensionCString_Len);
#ifdef DEBUG_profile_verbose
printf("directory name: %s\n",saltStr.get());
#endif
rv = aDir->AppendNative(saltStr);
NS_ENSURE_SUCCESS(rv,rv);
exists = PR_FALSE;
rv = aDir->Exists(&exists);
NS_ENSURE_SUCCESS(rv,rv);
if (!exists) {
rv = aDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
NS_ENSURE_SUCCESS(rv,rv);
}
return NS_OK;
}
nsresult nsProfile::IsProfileDirSalted(nsIFile *profileDir, PRBool *isSalted)
{
nsresult rv;
NS_ENSURE_ARG_POINTER(isSalted);
*isSalted = PR_FALSE;
// 1. The name of the profile dir has to end in ".slt"
nsCAutoString leafName;
rv = profileDir->GetNativeLeafName(leafName);
if (NS_FAILED(rv)) return rv;
PRBool endsWithSalt = PR_FALSE;
if (leafName.Length() >= kSaltExtensionCString_Len)
{
nsReadingIterator<char> stringEnd;
leafName.EndReading(stringEnd);
nsReadingIterator<char> stringStart = stringEnd;
stringStart.advance( -(NS_STATIC_CAST(PRInt32, kSaltExtensionCString_Len)) );
endsWithSalt =
Substring(stringStart, stringEnd).Equals(kSaltExtensionCString);
}
if (!endsWithSalt)
return NS_OK;
// 2. The profile dir has to be its parent's only child.
nsCOMPtr<nsIFile> parentDir;
rv = profileDir->GetParent(getter_AddRefs(parentDir));
if (NS_FAILED(rv)) return rv;
PRBool hasMore;
nsCOMPtr<nsISimpleEnumerator> dirIterator;
rv = parentDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
if (NS_FAILED(rv)) return rv;
PRInt32 numChildren = 0;
rv = dirIterator->HasMoreElements(&hasMore);
while (NS_SUCCEEDED(rv) && hasMore && numChildren <= 1) {
nsCOMPtr<nsIFile> child;
rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(child));
if (NS_SUCCEEDED(rv))
++numChildren;
rv = dirIterator->HasMoreElements(&hasMore);
}
if (NS_SUCCEEDED(rv) && numChildren == 1)
*isSalted = PR_TRUE;
return NS_OK;
}
/*
* Setters
*/
// Sets the current profile directory
nsresult nsProfile::SetProfileDir(const PRUnichar *profileName, nsIFile *profileDir)
{
NS_ENSURE_ARG(profileName);
NS_ENSURE_ARG(profileDir);
nsresult rv = NS_OK;
// Need to ensure that this directory exists
PRBool exists;
rv = profileDir->Exists(&exists);
if (NS_SUCCEEDED(rv) && !exists)
rv = profileDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(profileDir));
NS_ENSURE_TRUE(localFile, NS_ERROR_FAILURE);
ProfileStruct* aProfile = new ProfileStruct();
NS_ENSURE_TRUE(aProfile, NS_ERROR_OUT_OF_MEMORY);
aProfile->profileName = profileName;
aProfile->SetResolvedProfileDir(localFile);
aProfile->isMigrated = PR_TRUE;
aProfile->isImportType = PR_FALSE;
// convert "now" from microsecs to millisecs
PRInt64 oneThousand = LL_INIT(0, 1000);
PRInt64 nowInMilliSecs = PR_Now();
LL_DIV(aProfile->creationTime, nowInMilliSecs, oneThousand);
gProfileDataAccess->SetValue(aProfile);
delete aProfile;
return rv;
}
// Creates a new profile with UILocale and contentLocale
NS_IMETHODIMP
nsProfile::CreateNewProfileWithLocales(const PRUnichar* profileName,
const PRUnichar* nativeProfileDir,
const PRUnichar* aUILocale,
const PRUnichar* aContentLocale,
PRBool useExistingDir)
{
NS_ENSURE_ARG_POINTER(profileName);
nsresult rv = NS_OK;
#if defined(DEBUG_profile)
{
printf("ProfileManager : CreateNewProfileWithLocales\n");
printf("Profile Name: %s\n", NS_LossyConvertUTF16toASCII(profileName).get());
if (nativeProfileDir)
printf("Profile Dir: %s\n", NS_LossyConvertUTF16toASCII(nativeProfileDir).get());
}
#endif
nsCOMPtr<nsIFile> profileDir;
PRBool exists;
if (!nativeProfileDir)
{
// They didn't specify a directory path...
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILES_ROOT_DIR, getter_AddRefs(profileDir));
if (NS_FAILED(rv)) return rv;
rv = profileDir->Exists(&exists);
if (NS_FAILED(rv)) return rv;
if (!exists)
profileDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
// append profile name
profileDir->Append(nsDependentString(profileName));
}
else {
rv = NS_NewLocalFile(nsDependentString(nativeProfileDir), PR_TRUE, (nsILocalFile **)((nsIFile **)getter_AddRefs(profileDir)));
if (NS_FAILED(rv)) return rv;
// this prevents people from choosing their profile directory
// or another directory, and remove it when they delete the profile.
// append profile name
profileDir->Append(nsDependentString(profileName));
}
// Make profile directory unique only when the user
// decides to not use an already existing profile directory
if (!useExistingDir) {
rv = profileDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_FAILED(rv)) return rv;
}
#if defined(DEBUG_profile_verbose)
printf("before SetProfileDir\n");
#endif
rv = profileDir->Exists(&exists);
if (NS_FAILED(rv)) return rv;
if (!exists)
{
rv = profileDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_FAILED(rv)) return rv;
useExistingDir = PR_FALSE;
}
// since the directory didn't exist, add the indirection
rv = AddLevelOfIndirection(profileDir);
if (NS_FAILED(rv)) return rv;
// Set the directory value and add the entry to the registry tree.
rv = SetProfileDir(profileName, profileDir);
#if defined(DEBUG_profile_verbose)
printf("after SetProfileDir\n");
#endif
// Get profile defaults folder..
nsCOMPtr <nsIFile> profDefaultsDir;
rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR, getter_AddRefs(profDefaultsDir));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIChromeRegistrySea> chromeRegistry =
do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCAutoString uiLocale, contentLocale;
LossyCopyUTF16toASCII(aUILocale, uiLocale);
LossyCopyUTF16toASCII(aContentLocale, contentLocale);
// When aUILocale == null or aContentLocale == null, set those
// from default values which are from default or from command
// line options
// This fallback is for CreateNewProfile() of
// CreateDefaultProfile() and ProcessArgs() Those functions
// call CreateNewProfile(locale=null). We should consider
// default values or specified values of locales for
// CreateDefaultProfile() and ProcessArgs().
// We can get preferred UILocale and contentLocale (specified
// -UILocale and -contentLocale) by GetSelectedLocale() which
// is done in nsAppRunner.cpp::InstallGlobalLocale()
nsCOMPtr<nsIChromeRegistrySea> packageRegistry = do_QueryInterface(chromeRegistry);
if (uiLocale.IsEmpty() && packageRegistry) {
nsCAutoString currentUILocaleName;
rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global"),
currentUILocaleName);
if (NS_SUCCEEDED(rv)) {
uiLocale = currentUILocaleName;
}
}
if (contentLocale.IsEmpty()) {
nsCAutoString currentContentLocaleName;
rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global-region"),
currentContentLocaleName);
if (NS_SUCCEEDED(rv)) {
contentLocale = currentContentLocaleName;
}
}
#if defined(DEBUG_profile_verbose)
printf(" uiLocale=%s\n", uiLocale.get());
printf(" contentLocale=%s\n", contentLocale.get());
#endif
nsCAutoString pathBuf;
rv = profileDir->GetNativePath(pathBuf);
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString fileStr;
rv = NS_GetURLSpecFromFile(profileDir, fileStr);
if (NS_FAILED(rv)) return rv;
if (!uiLocale.IsEmpty()) {
rv = chromeRegistry->SelectLocaleForProfile(uiLocale,
NS_ConvertUTF8toUTF16(fileStr).get());
// Remember which profile has been created with the UILocale
// didn't use gProfileDataAccess because just needed one time
if (NS_SUCCEEDED(rv)) {
nsCStringKey key(pathBuf);
gLocaleProfiles->Put(&key, (void*)PR_TRUE);
}
}
//need to set skin info here
nsCAutoString currentSkinName;
rv = packageRegistry->GetSelectedSkin(NS_LITERAL_CSTRING("global"),currentSkinName);
if (!currentSkinName.IsEmpty()) {
rv = chromeRegistry->SelectSkinForProfile(currentSkinName,
NS_ConvertUTF8toUTF16(fileStr).get());
}
if (!contentLocale.IsEmpty()) {
// caller prefers locale subdir
nsCOMPtr<nsIFile> locProfDefaultsDir;
rv = profDefaultsDir->Clone(getter_AddRefs(locProfDefaultsDir));
if (NS_FAILED(rv)) return rv;
locProfDefaultsDir->AppendNative(contentLocale);
rv = locProfDefaultsDir->Exists(&exists);
if (NS_SUCCEEDED(rv) && exists) {
profDefaultsDir = locProfDefaultsDir; // transfers ownership
#if defined(DEBUG_profile_verbose)
nsCAutoString profilePath;
rv = profDefaultsDir->GetNativePath(profilePath);
if (NS_SUCCEEDED(rv))
printf(" profDefaultsDir is set to: %s\n", profilePath.get());
#endif
}
rv = chromeRegistry->SelectLocaleForProfile(contentLocale,
NS_ConvertUTF8toUTF16(fileStr).get());
// Remember which profile has been created with the UILocale
// didn't use gProfileDataAccess because just needed one time
if (NS_SUCCEEDED(rv)) {
nsCStringKey key(pathBuf);
gLocaleProfiles->Put(&key, (void*)PR_TRUE);
}
}
}
// Copy contents from defaults folder.
rv = profDefaultsDir->Exists(&exists);
if (NS_SUCCEEDED(rv) && exists && (!useExistingDir))
{
RecursiveCopy(profDefaultsDir, profileDir);
}
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
gProfileDataAccess->UpdateRegistry(nsnull);
return NS_OK;
}
// Rename a old profile to new profile.
// Copies all the keys from old profile to new profile.
// Creates a new profile
NS_IMETHODIMP
nsProfile::CreateNewProfile(const PRUnichar* profileName,
const PRUnichar* nativeProfileDir,
const PRUnichar* langcode,
PRBool useExistingDir)
{
return CreateNewProfileWithLocales(profileName,nativeProfileDir,langcode,nsnull,useExistingDir);
}
// Rename a old profile to new profile.
// Copies all the keys from old profile to new profile.
// Deletes the old profile from the registry
NS_IMETHODIMP
nsProfile::RenameProfile(const PRUnichar* oldName, const PRUnichar* newName)
{
NS_ENSURE_ARG_POINTER(oldName);
NS_ENSURE_ARG_POINTER(newName);
nsresult rv = NS_OK;
#if defined(DEBUG_profile)
{
printf("ProfileManager : Renaming profile\n");
nsCAutoString temp1; temp1.AssignWithConversion(oldName);
printf("Old name: %s\n", NS_LossyConvertUTF16toASCII(oldName).get());
nsCAutoString temp2; temp2.AssignWithConversion(newName);
printf("New name: %s\n", NS_LossyConvertUTF16toASCII(newName).get());
}
#endif
PRBool exists;
rv = ProfileExists(newName, &exists);
if (NS_FAILED(rv)) return rv;
// That profile already exists...
if (exists) {
#if defined(DEBUG_profile)
printf("ProfileManager : Rename Operation failed : Profile exists. Provide a different new name for profile.\n");
#endif
return NS_ERROR_FAILURE;
}
PRBool renamedIsCurrent = mCurrentProfileName.Equals(oldName);
// Copy reg keys
rv = CopyRegKey(oldName, newName);
if (NS_FAILED(rv)) return rv;
// Delete old profile entry
rv = DeleteProfile(oldName, PR_FALSE /* don't delete files */);
// If the profile being renamed is the current profile,
// change the current profile name.
if (renamedIsCurrent) {
gProfileDataAccess->SetCurrentProfile(newName);
gProfileDataAccess->mForgetProfileCalled = PR_FALSE;
mCurrentProfileName.Assign(newName);
mCurrentProfileAvailable = PR_TRUE;
}
if (NS_FAILED(rv)) return rv;
/* note, we do not rename the directory on disk to the new name
* this is on purpose.
*
* we don't require the directory name to match the profile name,
* but it usually does.
* (the pairing of values occurs in the profile registry)
*
* Imagine this scenario:
* 1) user creates a profile "foo" and the directory gets named "foo".
* 2) user creates a profile "bar" and the directory gets named "bar"
* 3) user deletes the profile "foo", but chooses not to delete the files on disk. (they are given this option when deleting a profile)
* 4) user renames "bar" profile to "foo", but still uses the directory named "bar" on disk
*
* bad things would happen if we tried to rename the directory
*/
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
gProfileDataAccess->UpdateRegistry(nsnull);
return NS_OK;
}
// Copy old profile entries to the new profile
// In the process creates new profile subtree.
nsresult nsProfile::CopyRegKey(const PRUnichar *oldProfile, const PRUnichar *newProfile)
{
NS_ENSURE_ARG_POINTER(oldProfile);
NS_ENSURE_ARG_POINTER(newProfile);
nsresult rv = NS_OK;
ProfileStruct *aProfile;
rv = gProfileDataAccess->GetValue(oldProfile, &aProfile);
if (NS_FAILED(rv)) return rv;
aProfile->profileName = newProfile;
rv = gProfileDataAccess->SetValue(aProfile);
delete aProfile;
return rv;
}
NS_IMETHODIMP nsProfile::ForgetCurrentProfile()
{
nsresult rv = NS_OK;
// Remove the current profile subtree from the registry.
PRUnichar tmp[] = { '\0' };
gProfileDataAccess->SetCurrentProfile(tmp);
if (NS_FAILED(rv)) return rv;
gProfileDataAccess->mForgetProfileCalled = PR_TRUE;
mCurrentProfileAvailable = PR_FALSE;
mCurrentProfileName.Truncate(0);
return rv;
}
// Delete a profile from the registry
// Not deleting the directories on the harddisk yet.
// 4.x kind of confirmation need to be implemented yet
NS_IMETHODIMP nsProfile::DeleteProfile(const PRUnichar* profileName, PRBool canDeleteFiles)
{
NS_ENSURE_ARG_POINTER(profileName);
nsresult rv;
nsXPIDLString currProfile;
rv = GetCurrentProfile(getter_Copies(currProfile));
if (NS_SUCCEEDED(rv) && !nsCRT::strcmp(profileName, currProfile)) {
rv = ForgetCurrentProfile();
if (NS_FAILED(rv)) return rv;
}
rv = NS_OK;
// If user asks for it, delete profile directory
if (canDeleteFiles) {
nsCOMPtr<nsIFile> profileDir;
rv = GetProfileDir(profileName, getter_AddRefs(profileDir));
if (NS_FAILED(rv)) return rv;
PRBool exists;
rv = profileDir->Exists(&exists);
if (NS_FAILED(rv)) return rv;
if (exists) {
// The profile dir may be located inside a salted dir.
// If so, according to IsProfileDirSalted,
// delete the parent dir as well.
nsCOMPtr<nsIFile> dirToDelete(profileDir);
PRBool isSalted;
rv = IsProfileDirSalted(profileDir, &isSalted);
if (NS_SUCCEEDED(rv) && isSalted) {
nsCOMPtr<nsIFile> parentDir;
rv = profileDir->GetParent(getter_AddRefs(parentDir));
if (NS_SUCCEEDED(rv))
dirToDelete = parentDir;
}
rv = dirToDelete->Remove(PR_TRUE);
}
}
// Remove the subtree from the registry.
gProfileDataAccess->RemoveSubTree(profileName);
if (NS_FAILED(rv)) return rv;
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
gProfileDataAccess->UpdateRegistry(nsnull);
return rv;
}
NS_IMETHODIMP nsProfile::GetProfileList(PRUint32 *length, PRUnichar ***profileNames)
{
NS_ENSURE_ARG_POINTER(length);
*length = 0;
NS_ENSURE_ARG_POINTER(profileNames);
*profileNames = nsnull;
return gProfileDataAccess->GetProfileList(nsIProfileInternal::LIST_ONLY_NEW, length, profileNames);
}
NS_IMETHODIMP nsProfile::GetProfileListX(PRUint32 whichKind, PRUint32 *length, PRUnichar ***profileNames)
{
NS_ENSURE_ARG_POINTER(length);
*length = 0;
NS_ENSURE_ARG_POINTER(profileNames);
*profileNames = nsnull;
if (whichKind == nsIProfileInternal::LIST_FOR_IMPORT)
Update4xProfileInfo();
return gProfileDataAccess->GetProfileList(whichKind, length, profileNames);
}
// this will add all the 4x profiles (with isImportType flag set) to the
// profiles list. The adding of profiles is done only once per session.
nsresult nsProfile::Update4xProfileInfo()
{
nsresult rv = NS_OK;
#ifndef XP_BEOS
nsCOMPtr<nsIFile> oldRegFile;
rv = GetOldRegLocation(getter_AddRefs(oldRegFile));
if (NS_SUCCEEDED(rv))
rv = gProfileDataAccess->Get4xProfileInfo(oldRegFile, PR_TRUE);
#endif
return rv;
}
nsresult nsProfile::LoadNewProfilePrefs()
{
nsresult rv;
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
prefs->ResetUserPrefs();
prefs->ReadUserPrefs(nsnull);
return NS_OK;
}
nsresult nsProfile::GetOldRegLocation(nsIFile **aOldRegFile)
{
NS_ENSURE_ARG_POINTER(aOldRegFile);
*aOldRegFile = nsnull;
nsresult rv = NS_OK;
// For XP_UNIX there was no registry for 4.x profiles.
// Return nsnull for the file, and NS_OK - we succeeded in doing nothing.
#if defined(XP_WIN) || defined(XP_OS2) || defined(XP_MAC) || defined(XP_MACOSX)
nsCOMPtr<nsIFile> oldRegFile;
#if defined(XP_WIN)
rv = NS_GetSpecialDirectory(NS_WIN_WINDOWS_DIR, getter_AddRefs(oldRegFile));
#elif defined(XP_OS2)
rv = NS_GetSpecialDirectory(NS_OS2_DIR, getter_AddRefs(oldRegFile));
#elif defined(XP_MAC) || defined(XP_MACOSX)
rv = NS_GetSpecialDirectory(NS_MAC_PREFS_DIR, getter_AddRefs(oldRegFile));
#endif
if (NS_FAILED(rv))
return rv;
rv = oldRegFile->AppendNative(nsDependentCString(OLD_REGISTRY_FILE_NAME));
if (NS_FAILED(rv))
return rv;
NS_ADDREF(*aOldRegFile = oldRegFile);
#endif
return rv;
}
nsresult nsProfile::UpdateCurrentProfileModTime(PRBool updateRegistry)
{
nsresult rv;
// convert "now" from microsecs to millisecs
PRInt64 oneThousand = LL_INIT(0, 1000);
PRInt64 nowInMilliSecs = PR_Now();
LL_DIV(nowInMilliSecs, nowInMilliSecs, oneThousand);
rv = gProfileDataAccess->SetProfileLastModTime(mCurrentProfileName.get(), nowInMilliSecs);
if (NS_SUCCEEDED(rv) && updateRegistry) {
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
gProfileDataAccess->UpdateRegistry(nsnull);
}
return rv;
}
// Migrate profile information from the 4x registry to 5x registry.
NS_IMETHODIMP nsProfile::MigrateProfileInfo()
{
nsresult rv = NS_OK;
#ifndef XP_BEOS
nsCOMPtr<nsIFile> oldRegFile;
rv = GetOldRegLocation(getter_AddRefs(oldRegFile));
if (NS_SUCCEEDED(rv)) {
rv = gProfileDataAccess->Get4xProfileInfo(oldRegFile, PR_FALSE);
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
gProfileDataAccess->UpdateRegistry(nsnull);
}
#endif
return rv;
}
nsresult
nsProfile::CopyDefaultFile(nsIFile *profDefaultsDir, nsIFile *newProfDir, const nsACString &fileName)
{
nsresult rv;
nsCOMPtr<nsIFile> defaultFile;
PRBool exists;
rv = profDefaultsDir->Clone(getter_AddRefs(defaultFile));
if (NS_FAILED(rv)) return rv;
defaultFile->AppendNative(fileName);
rv = defaultFile->Exists(&exists);
if (NS_FAILED(rv)) return rv;
if (exists)
rv = defaultFile->CopyToNative(newProfDir, fileName);
else
rv = NS_ERROR_FILE_NOT_FOUND;
return rv;
}
nsresult
nsProfile::DefineLocaleDefaultsDir()
{
nsresult rv;
nsCOMPtr<nsIProperties> directoryService =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
NS_ENSURE_TRUE(directoryService, NS_ERROR_FAILURE);
nsCOMPtr<nsIFile> localeDefaults;
rv = directoryService->Get(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(localeDefaults));
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIChromeRegistrySea> packageRegistry =
do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
{
nsCAutoString localeName;
rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global-region"), localeName);
if (NS_SUCCEEDED(rv))
rv = localeDefaults->AppendNative(localeName);
}
rv = directoryService->Set(NS_APP_PROFILE_DEFAULTS_50_DIR, localeDefaults);
}
return rv;
}
// Migrate a selected profile
// Set the profile to the current profile....debatable.
// Calls PrefMigration service to do the Copy and Diverge
// of 4x Profile information
nsresult
nsProfile::MigrateProfileInternal(const PRUnichar* profileName,
nsIFile* oldProfDir,
nsIFile* newProfDir)
{
NS_ENSURE_ARG_POINTER(profileName);
#if defined(DEBUG_profile)
printf("Inside Migrate Profile routine.\n" );
#endif
// Call migration service to do the work.
nsresult rv;
nsCOMPtr <nsIPrefMigration> pPrefMigrator =
do_CreateInstance(kPrefMigrationCID, &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsILocalFile> oldProfDirLocal(do_QueryInterface(oldProfDir, &rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsILocalFile> newProfDirLocal(do_QueryInterface(newProfDir, &rv));
if (NS_FAILED(rv)) return rv;
nsCAutoString oldProfDirStr;
nsCAutoString newProfDirStr;
rv = oldProfDirLocal->GetPersistentDescriptor(oldProfDirStr);
if (NS_FAILED(rv)) return rv;
rv = newProfDirLocal->GetPersistentDescriptor(newProfDirStr);
if (NS_FAILED(rv)) return rv;
// you can do this a bunch of times.
rv = pPrefMigrator->AddProfilePaths(oldProfDirStr.get(), newProfDirStr.get());
rv = pPrefMigrator->ProcessPrefs(PR_TRUE); // param is ignored
if (NS_FAILED(rv)) return rv;
// check for diskspace errors
nsresult errorCode;
errorCode = pPrefMigrator->GetError();
// In either of the cases below we have to return error to make
// app understand that migration has failed.
if (errorCode == MIGRATION_CREATE_NEW)
{
PRInt32 numProfiles = 0;
ShowProfileWizard();
// When the automigration process fails because of disk space error,
// we present user a create profile wizard if the user chooses to create a
// a profile then. But then the user may click on cancel on that dialog...
// So, if the user clicks on cancel, the number of profiles should be
// ZERO at the point for the user who failed to automigrate single 4x profile.
// On such condition, set mDiskSpaceErrorQuitCalled to allow user to quit the app.
// If the user is presented with profilemanager dialog with multiple 4x profiles
// to migrate, value of mDiskSpaceErrorQuitCalled does not matter as it gets ignored..
// If a single profile needs automigration and no confirmation
// is needed for that operation mAutomigrate is set to false.
if (!mAutomigrate)
{
GetProfileCount(&numProfiles);
if (numProfiles == 0)
mDiskSpaceErrorQuitCalled = PR_TRUE;
}
mOutofDiskSpace = PR_TRUE;
return NS_ERROR_FAILURE;
}
else if (errorCode == MIGRATION_CANCEL)
{
// When the automigration process fails because of disk space error,
// user may choose to simply quit the app from the dialog presented
// by pref-migrator. So, set mDiskSpaceErrorQuitCalled to allow user
// to quit the app in such a case.
// If the user is presented with profilemanager dialog with multiple 4x profiles
// to migrate, value of mDiskSpaceErrorQuitCalled does not matter as it gets ignored..
// If a single profile needs automigration and no confirmation
// is needed for that operation mAutomigrate is set to false.
if (!mAutomigrate)
mDiskSpaceErrorQuitCalled = PR_TRUE;
ForgetCurrentProfile();
mOutofDiskSpace = PR_TRUE;
return NS_ERROR_FAILURE;
}
else if (errorCode != MIGRATION_SUCCESS)
{
return NS_ERROR_FAILURE;
}
// No longer copying the default 5.0 profile files into
// the migrated profile. Check for them as requested.
rv = SetProfileDir(profileName, newProfDir);
if (NS_FAILED(rv)) return rv;
gProfileDataAccess->SetMigratedFromDir(profileName, oldProfDirLocal);
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
gProfileDataAccess->UpdateRegistry(nsnull);
return rv;
}
NS_IMETHODIMP
nsProfile::MigrateProfile(const PRUnichar* profileName)
{
NS_ENSURE_ARG(profileName);
nsresult rv = NS_OK;
nsCOMPtr<nsIFile> oldProfDir;
nsCOMPtr<nsIFile> newProfDir;
nsCOMPtr<nsIPrefBranch> prefBranch;
nsXPIDLCString profMigDir;
rv = GetProfileDir(profileName, getter_AddRefs(oldProfDir));
if (NS_FAILED(rv))
return rv;
// Check PREF_MIGRATION_BEHAVIOR for how to set profiles root
// 0 - use NS_APP_USER_PROFILES_ROOT_DIR
// 1 - create one based on the NS4.x profile root
// 2 - use if not empty PREF_MIGRATION_DIRECTORY
PRInt32 profRootBehavior = 0;
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
rv = prefs->GetBranch(nsnull, getter_AddRefs(prefBranch));
if (NS_SUCCEEDED(rv))
(void) prefBranch->GetIntPref(PREF_MIGRATION_BEHAVIOR, &profRootBehavior);
}
switch (profRootBehavior) {
case 1:
rv = oldProfDir->Clone(getter_AddRefs(newProfDir));
if (NS_FAILED(rv))
return rv;
rv = newProfDir->SetNativeLeafName(NS_LITERAL_CSTRING("Profiles"));
if (NS_FAILED(rv))
return rv;
break;
case 2:
rv = prefBranch->GetCharPref(PREF_MIGRATION_DIRECTORY, getter_Copies(profMigDir));
if (NS_SUCCEEDED(rv) && !profMigDir.IsEmpty()) {
nsCOMPtr<nsILocalFile> localFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
rv = localFile->InitWithNativePath(nsDependentCString(profMigDir));
if (NS_SUCCEEDED(rv)) {
newProfDir = do_QueryInterface(localFile, &rv);
if (NS_FAILED(rv))
return rv;
}
}
break;
default:
break;
}
if (!newProfDir) {
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILES_ROOT_DIR, getter_AddRefs(newProfDir));
if (NS_FAILED(rv))
return rv;
}
rv = newProfDir->Append(nsDependentString(profileName));
if (NS_FAILED(rv))
return rv;
rv = newProfDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_FAILED(rv))
return rv;
// always create level indirection when migrating
rv = AddLevelOfIndirection(newProfDir);
if (NS_FAILED(rv))
return rv;
return MigrateProfileInternal(profileName, oldProfDir, newProfDir);
}
NS_IMETHODIMP
nsProfile::RemigrateProfile(const PRUnichar* profileName)
{
NS_ENSURE_ARG_POINTER(profileName);
nsCOMPtr<nsIFile> profileDir;
nsresult rv = GetProfileDir(profileName, getter_AddRefs(profileDir));
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIFile> newProfileDir;
rv = profileDir->Clone(getter_AddRefs(newProfileDir));
NS_ENSURE_SUCCESS(rv,rv);
// The profile list used by GetOriginalProfileDir is the one with ALL 4.x
// profiles - even ones for which there's a moz profile of the same name.
nsCOMPtr<nsILocalFile> oldProfileDir;
rv = GetOriginalProfileDir(profileName, getter_AddRefs(oldProfileDir));
NS_ENSURE_SUCCESS(rv,rv);
// In case of error, we'll restore what we've renamed.
nsCAutoString origDirLeafName;
rv = profileDir->GetNativeLeafName(origDirLeafName);
NS_ENSURE_SUCCESS(rv,rv);
// leave <xxxxxxxx>.slt alone,
// and remigrate the 4.x profile into <xxxxxxxx>.slt-new
nsCAutoString newDirLeafName(origDirLeafName + NS_LITERAL_CSTRING("-new"));
rv = newProfileDir->SetNativeLeafName(newDirLeafName);
NS_ENSURE_SUCCESS(rv,rv);
// Create a new directory for the remigrated profile
rv = newProfileDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create new directory for the remigrated profile");
if (NS_SUCCEEDED(rv)) {
rv = MigrateProfileInternal(profileName, oldProfileDir, newProfileDir);
NS_ASSERTION(NS_SUCCEEDED(rv), "MigrateProfileInternal failed");
}
return rv;
}
nsresult
nsProfile::ShowProfileWizard(void)
{
nsresult rv;
nsCOMPtr<nsIWindowWatcher> windowWatcher(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDialogParamBlock> ioParamBlock(do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID, &rv));
if (NS_FAILED(rv)) return rv;
ioParamBlock->SetInt(0,4); // standard wizard buttons
nsCOMPtr<nsIDOMWindow> newWindow;
rv = windowWatcher->OpenWindow(nsnull,
PROFILE_WIZARD_URL,
"_blank",
kDefaultOpenWindowParams,
ioParamBlock,
getter_AddRefs(newWindow));
return rv;
}
NS_IMETHODIMP nsProfile::ProfileExists(const PRUnichar *profileName, PRBool *exists)
{
NS_ENSURE_ARG_POINTER(profileName);
NS_ENSURE_ARG_POINTER(exists);
*exists = gProfileDataAccess->ProfileExists(profileName);
return NS_OK;
}
NS_IMETHODIMP nsProfile::IsCurrentProfileAvailable(PRBool *available)
{
NS_ENSURE_ARG_POINTER(available);
*available = mCurrentProfileAvailable;
return NS_OK;
}
// Gets the number of unmigrated 4x profiles
// Location: Common/Profiles
NS_IMETHODIMP nsProfile::Get4xProfileCount(PRInt32 *numProfiles)
{
NS_ENSURE_ARG_POINTER(numProfiles);
*numProfiles = 0;
gProfileDataAccess->GetNum4xProfiles(numProfiles);
return NS_OK;
}
// Migrates all unmigrated profiles
NS_IMETHODIMP nsProfile::MigrateAllProfiles()
{
nsresult rv;
PRUint32 numOldProfiles = 0;
PRUnichar **nameArray = nsnull;
rv = GetProfileListX(nsIProfileInternal::LIST_ONLY_OLD, &numOldProfiles, &nameArray);
if (NS_FAILED(rv)) return rv;
for (PRUint32 i = 0; i < numOldProfiles; i++)
{
rv = MigrateProfile(nameArray[i]);
if (NS_FAILED(rv)) break;
}
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numOldProfiles, nameArray);
return rv;
}
NS_IMETHODIMP nsProfile::CloneProfile(const PRUnichar* newProfile)
{
NS_ENSURE_ARG_POINTER(newProfile);
nsresult rv = NS_OK;
#if defined(DEBUG_profile)
printf("ProfileManager : CloneProfile\n");
#endif
nsCOMPtr<nsIFile> currProfileDir;
rv = GetCurrentProfileDir(getter_AddRefs(currProfileDir));
if (NS_FAILED(rv)) return rv;
PRBool exists;
rv = currProfileDir->Exists(&exists);
if (NS_SUCCEEDED(rv) && exists)
{
nsCOMPtr<nsIFile> aFile;
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILES_ROOT_DIR, getter_AddRefs(aFile));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsILocalFile> destDir(do_QueryInterface(aFile, &rv));
if (NS_FAILED(rv)) return rv;
destDir->AppendRelativePath(nsDependentString(newProfile));
// Find a unique name in the dest dir
rv = destDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_FAILED(rv)) return rv;
rv = RecursiveCopy(currProfileDir, destDir);
if (NS_FAILED(rv)) return rv;
rv = SetProfileDir(newProfile, destDir);
}
#if defined(DEBUG_profile_verbose)
{
if (NS_SUCCEEDED(rv))
printf("ProfileManager : Cloned CurrentProfile\n");
nsCAutoString temp; temp.AssignWithConversion(newProfile);
printf("The new profile is ->%s<-\n", temp.get());
}
#endif
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
return rv;
}
nsresult
nsProfile::CreateDefaultProfile(void)
{
nsresult rv = NS_OK;
// Get the default user profiles folder
nsCOMPtr<nsIFile> profileRootDir;
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILES_ROOT_DIR, getter_AddRefs(profileRootDir));
if (NS_FAILED(rv)) return rv;
nsAutoString profilePath;
rv = profileRootDir->GetPath(profilePath);
if (NS_FAILED(rv)) return rv;
rv = CreateNewProfile(DEFAULT_PROFILE_NAME, profilePath.get(), nsnull, PR_TRUE);
return rv;
}
NS_IMETHODIMP
nsProfile::UpdateRegistry(nsIFile* regName)
{
nsresult rv = NS_OK;
gProfileDataAccess->mProfileDataChanged = PR_TRUE;
rv= gProfileDataAccess->UpdateRegistry(regName);
return rv;
}
NS_IMETHODIMP
nsProfile::SetRegStrings(const PRUnichar* profileName,
const PRUnichar* regString,
const PRUnichar* regName,
const PRUnichar* regEmail,
const PRUnichar* regOption)
{
nsresult rv = NS_OK;
ProfileStruct* aProfile;
rv = gProfileDataAccess->GetValue(profileName, &aProfile);
if (NS_FAILED(rv)) return rv;
if (aProfile == nsnull)
return NS_ERROR_FAILURE;
aProfile->NCHavePregInfo = regString;
if (regName) aProfile->NCProfileName = regName;
if (regEmail) aProfile->NCEmailAddress = regEmail;
if (regOption) aProfile->NCDeniedService = regOption;
gProfileDataAccess->SetValue(aProfile);
delete aProfile;
return rv;
}
NS_IMETHODIMP
nsProfile::IsRegStringSet(const PRUnichar *profileName, char **regString)
{
NS_ENSURE_ARG_POINTER(profileName);
NS_ENSURE_ARG_POINTER(regString);
gProfileDataAccess->CheckRegString(profileName, regString);
return NS_OK;
}
/*
* nsIProfileChangeStatus Implementation
*/
NS_IMETHODIMP nsProfile::VetoChange()
{
mProfileChangeVetoed = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP nsProfile::ChangeFailed()
{
mProfileChangeFailed = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsProfile::GetRegStrings(const PRUnichar *aProfileName,
PRUnichar **aRegString,
PRUnichar **aRegName,
PRUnichar **aRegEmail,
PRUnichar **aRegOption)
{
NS_ENSURE_ARG_POINTER(aProfileName);
NS_ENSURE_ARG_POINTER(aRegString);
NS_ENSURE_ARG_POINTER(aRegName);
NS_ENSURE_ARG_POINTER(aRegEmail);
NS_ENSURE_ARG_POINTER(aRegOption);
ProfileStruct* profileVal;
nsresult rv = gProfileDataAccess->GetValue(aProfileName, &profileVal);
if (NS_FAILED(rv))
return rv;
if (profileVal == nsnull)
return NS_ERROR_FAILURE;
*aRegString = ToNewUnicode(profileVal->NCHavePregInfo);
if (!*aRegString)
return NS_ERROR_OUT_OF_MEMORY;
*aRegName = ToNewUnicode(profileVal->NCProfileName);
if (!*aRegName)
return NS_ERROR_OUT_OF_MEMORY;
*aRegEmail = ToNewUnicode(profileVal->NCEmailAddress);
if (!*aRegEmail)
return NS_ERROR_OUT_OF_MEMORY;
*aRegOption = ToNewUnicode(profileVal->NCDeniedService);
if (!*aRegOption)
return NS_ERROR_OUT_OF_MEMORY;
delete profileVal;
return NS_OK;
}
nsresult nsProfile::GetLocalProfileDir(const PRUnichar* aProfileName,
nsIFile** aLocalDir)
{
*aLocalDir = nsnull;
nsresult rv;
nsCOMPtr<nsIProperties> directoryService =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIFile> localDir;
rv = directoryService->Get(NS_APP_USER_PROFILES_LOCAL_ROOT_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(localDir));
if (NS_FAILED(rv))
return rv;
rv = localDir->Append(nsDependentString(aProfileName));
if (NS_FAILED(rv))
return rv;
localDir.swap(*aLocalDir);
return NS_OK;
}