/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape 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/NPL/ * * 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 Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #include "nsProfile.h" #include "nsIPref.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 "nsIFileSpec.h" #include "nsIFileLocator.h" #include "nsFileLocations.h" #include "nsEscape.h" #include "nsIURL.h" #include "nsIAppShellService.h" #include "nsAppShellCIDs.h" #include "prprf.h" #include "nsIIOService.h" #include "nsNetUtil.h" #include "nsIPrefMigration.h" #include "nsPrefMigrationCIDs.h" #include "nsFileStream.h" #include "nsSpecialSystemDirectory.h" #include "nsIPrompt.h" #include "nsIStreamListener.h" #include "nsIServiceManager.h" #include "nsCOMPtr.h" #include "nsIBookmarksService.h" #include "nsIModule.h" #include "nsIGenericFactory.h" #include "nsICookieService.h" #include "nsICategoryManager.h" #include "nsISupportsPrimitives.h" // Interfaces Needed #include "nsIDocShell.h" #include "nsIWebShell.h" #include "nsIWebBrowserChrome.h" #if defined (XP_UNIX) #elif defined (XP_MAC) #define OLD_REGISTRY_FILE_NAME "Netscape Registry" #elif defined (XP_BEOS) #else /* assume XP_PC */ #ifndef XP_OS2 #include #endif #define OLD_REGISTRY_FILE_NAME "nsreg.dat" #endif /* XP_UNIX */ // hack for copying panels.rdf into migrated profile dir #define PANELS_RDF_FILE "panels.rdf" // A default profile name, in case automigration 4x profile fails #define DEFAULT_PROFILE_NAME "default" #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 CONFIRM_AUTOMIGRATE_URL "chrome://communicator/content/profile/confirmMigration.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 PREF_CONFIRM_AUTOMIGRATION "profile.confirm_automigration" #define SHRIMP_PREF "shrimp.startup.enable" #if defined (XP_MAC) #define CHROME_STYLE nsIWebBrowserChrome::windowBordersOn | nsIWebBrowserChrome::windowCloseOn | nsIWebBrowserChrome::centerScreen #else /* the rest */ #define CHROME_STYLE nsIWebBrowserChrome::allChrome | nsIWebBrowserChrome::centerScreen #endif // 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 #define DEBUG_profile 1 #undef DEBUG_profile_verbose // 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 gDataAccessInstCount = 0; static PRBool mCurrentProfileAvailable = PR_FALSE; // IID and CIDs of all the services needed static NS_DEFINE_CID(kIProfileIID, NS_IPROFILE_IID); static NS_DEFINE_CID(kBookmarksCID, NS_BOOKMARKS_SERVICE_CID); static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID); static NS_DEFINE_CID(kFileLocatorCID, NS_FILELOCATOR_CID); static NS_DEFINE_CID(kRegistryCID, NS_REGISTRY_CID); static NS_DEFINE_CID(kPrefCID, NS_PREF_CID); static NS_DEFINE_CID(kAppShellServiceCID, NS_APPSHELL_SERVICE_CID); static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID); static NS_DEFINE_IID(kIIOServiceIID, NS_IIOSERVICE_IID); static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); static NS_DEFINE_CID(kPrefMigrationCID, NS_PREFMIGRATION_CID); static NS_DEFINE_CID(kPrefConverterCID, NS_PREFCONVERTER_CID); static NS_DEFINE_IID(kCookieServiceCID, NS_COOKIESERVICE_CID); static nsresult GetStringFromSpec(nsFileSpec inSpec, char **string) { nsresult rv; nsCOMPtr spec; rv = NS_NewFileSpecWithSpec(inSpec, getter_AddRefs(spec)); if (NS_SUCCEEDED(rv)) { rv = spec->GetPersistentDescriptorString(string); if (NS_SUCCEEDED(rv)) { return NS_OK; } else { nsCRT::free(*string); return rv; } } else { *string = nsnull; return rv; } } /* * Constructor/Destructor */ nsProfile::nsProfile() { mAutomigrate = PR_FALSE; if(!gProfileDataAccess) gProfileDataAccess = new nsProfileAccess(); gDataAccessInstCount++; NS_INIT_REFCNT(); } nsProfile::~nsProfile() { #if defined(DEBUG_profile_verbose) printf("~nsProfile \n"); #endif gProfileDataAccess->mProfileDataChanged = PR_TRUE; gProfileDataAccess->UpdateRegistry(); gDataAccessInstCount--; if (gDataAccessInstCount == 0) delete gProfileDataAccess; } /* * 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_END /* * nsIProfile Implementation */ NS_IMETHODIMP nsProfile::Startup(const PRUnichar *filename) { return NS_OK; } 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) { nsresult rv; // 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 if (cmdLineArgs) rv = ProcessArgs(cmdLineArgs, &profileDirSet, profileURLStr); if (!profileDirSet) { rv = LoadDefaultProfileDir(profileURLStr); if (NS_FAILED(rv)) return rv; } #ifdef DEBUG_profile_verbose printf("Profile Manager : Profile Wizard and Manager activites : End\n"); #endif return NS_OK; } nsresult nsProfile::LoadDefaultProfileDir(nsCString & profileURLStr) { nsresult rv; nsCOMPtr profileURL; PRInt32 numProfiles=0; NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv); if (NS_FAILED(rv)) return rv; GetProfileCount(&numProfiles); /* * Create the Application Shell instance... */ NS_WITH_SERVICE(nsIAppShellService, profAppShell, kAppShellServiceCID, &rv); if (NS_FAILED(rv)) return rv; PRBool shrimpPrefEnabled = PR_FALSE; prefs->GetBoolPref(SHRIMP_PREF, &shrimpPrefEnabled); if (shrimpPrefEnabled) profileURLStr = ""; if (profileURLStr.Length() == 0) { // 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; GetProfileCount(&numProfiles); profileURLStr = ""; mCurrentProfileAvailable = PR_TRUE; // Need to load new profile prefs. rv = LoadNewProfilePrefs(); } else if (numProfiles > 1) profileURLStr = PROFILE_SELECTION_URL; } if ((profileURLStr.Length() != 0) && !(shrimpPrefEnabled)) { rv = NS_NewURI(getter_AddRefs(profileURL), (const char *)profileURLStr); if (NS_FAILED(rv)) { return rv; } nsCOMPtr profWindow; rv = profAppShell->CreateTopLevelWindow(nsnull, profileURL, PR_TRUE, PR_TRUE, CHROME_STYLE, NS_SIZETOCONTENT, // width NS_SIZETOCONTENT, // height getter_AddRefs(profWindow)); if (NS_FAILED(rv)) return rv; /* * Start up the main event loop... */ rv = profAppShell->Run(); } PRBool confirmAutomigration = PR_FALSE; if (NS_SUCCEEDED(rv) && prefs) { rv = prefs->GetBoolPref(PREF_CONFIRM_AUTOMIGRATION, &confirmAutomigration); if (NS_FAILED(rv)) confirmAutomigration = PR_FALSE; } if (confirmAutomigration) { if (profileURLStr == (const char*)CONFIRM_AUTOMIGRATE_URL) { PRBool automigrate = PR_FALSE; rv = GetAutomigrate(&automigrate); if (NS_SUCCEEDED(rv) && automigrate) { AutoMigrate(); } else { // the user hit cancel. // so they don't want to automatically migrate // so call this again with the profile manager ui nsCString profileManagerUrl(PROFILE_MANAGER_URL); rv = LoadDefaultProfileDir(profileManagerUrl); return rv; } } } // if we get here, and we don't have a current profile, // return a failure so we will exit // this can happen, if the user hits Exit in the profile manager dialog nsXPIDLString currentProfileStr; rv = GetCurrentProfile(getter_Copies(currentProfileStr)); if (NS_FAILED(rv) || (*(const PRUnichar*)currentProfileStr == 0)) { return NS_ERROR_FAILURE; } mCurrentProfileAvailable = PR_TRUE; NS_WITH_SERVICE(nsICategoryManager, catman, NS_CATEGORYMANAGER_PROGID, &rv); if(NS_SUCCEEDED(rv) && catman) { nsCOMPtr enumItem; rv = catman->EnumerateCategory(NS_PROFILE_STARTUP_CATEGORY, getter_AddRefs(enumItem)); if(NS_SUCCEEDED(rv) && enumItem) { while (PR_TRUE) { nsCOMPtr progid; rv = enumItem->GetNext(getter_AddRefs(progid)); if (NS_FAILED(rv) || !progid) break; nsXPIDLCString progidString; progid->ToString (getter_Copies(progidString)); nsCOMPtr listener = do_CreateInstance(progidString, &rv); if (listener) listener->OnProfileStartup(currentProfileStr); } } } // Now we have the right profile, read the user-specific prefs. rv = prefs->ReadUserPrefs(); if (NS_FAILED(rv)) return rv; nsCOMPtr pPrefConverter = do_CreateInstance(kPrefConverterCID, &rv); if (NS_FAILED(rv)) return rv; if (!pPrefConverter) return NS_ERROR_FAILURE; rv = pPrefConverter->ConvertPrefsToUTF8IfNecessary(); return rv; } nsresult nsProfile::AutoMigrate() { nsresult rv = NS_OK; // automatically migrate the one 4.x profile rv = MigrateAllProfiles(); if (NS_FAILED(rv)) { #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(); return rv; } nsresult nsProfile::ProcessArgs(nsICmdLineService *cmdLineArgs, PRBool* profileDirSet, nsCString & profileURLStr) { NS_ASSERTION(cmdLineArgs, "Invalid cmdLineArgs"); NS_ASSERTION(profileDirSet, "Invalid profileDirSet"); nsresult rv; nsXPIDLCString cmdResult; nsFileSpec currProfileDirSpec; #ifdef DEBUG_profile_verbose printf("Profile Manager : Command Line Options : Begin\n"); #endif // check for command line arguments for profile manager // // -P command line option works this way: // apprunner -P profilename // runs the app using the profile // remembers profile for next time rv = cmdLineArgs->GetCmdLineValue(PROFILE_CMD_LINE_ARG, getter_Copies(cmdResult)); if (NS_SUCCEEDED(rv)) { if (cmdResult) { nsAutoString currProfileName; currProfileName.AssignWithConversion(cmdResult); #ifdef DEBUG_profile printf("ProfileName : %s\n", (const char*)cmdResult); #endif /* DEBUG_profile */ PRBool exists; rv = ProfileExists(currProfileName.GetUnicode(), &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 = GetProfileDir(currProfileName.GetUnicode(), &currProfileDirSpec); if (NS_SUCCEEDED(rv)){ *profileDirSet = PR_TRUE; mCurrentProfileAvailable = PR_TRUE; // Need to load new profile prefs. rv = LoadNewProfilePrefs(); } } } } // -CreateProfile command line option works this way: // apprunner -CreateProfile profilename // creates a new profile named 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 and sets the directory to // 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) { nsAutoString currProfileName; currProfileName.AssignWithConversion(strtok(NS_CONST_CAST(char*,(const char*)cmdResult), " ")); nsAutoString currProfileDirString; currProfileDirString.AssignWithConversion(strtok(NULL, " ")); if (!currProfileDirString.IsEmpty()) { currProfileDirSpec = currProfileDirString; } else { // No directory name provided. Get File Locator NS_WITH_SERVICE(nsIFileLocator, locator, kFileLocatorCID, &rv); if (NS_FAILED(rv) || !locator) return NS_ERROR_FAILURE; // Get current profile, make the new one a sibling... nsCOMPtr spec; rv = locator->GetFileLocation( nsSpecialFileSpec::App_DefaultUserProfileRoot50, getter_AddRefs(spec)); if (NS_FAILED(rv) || !spec) return NS_ERROR_FAILURE; spec->GetFileSpec(&currProfileDirSpec); rv = locator->ForgetProfileDir(); } #ifdef DEBUG_profile_verbose printf("profileName & profileDir are: %s\n", (const char*)cmdResult); #endif /* DEBUG_profile */ nsAutoString currProfileDir; currProfileDir.AssignWithConversion(currProfileDirSpec.GetNativePathCString()); rv = CreateNewProfile(currProfileName.GetUnicode(), currProfileDir.GetUnicode(), nsnull, PR_TRUE); if (NS_SUCCEEDED(rv)) { *profileDirSet = PR_TRUE; mCurrentProfileAvailable = PR_TRUE; // Need to load new profile prefs. rv = LoadNewProfilePrefs(); gProfileDataAccess->mProfileDataChanged = PR_TRUE; gProfileDataAccess->UpdateRegistry(); } 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) { profileURLStr = PROFILE_MANAGER_URL; } } // Start Profile Selection rv = cmdLineArgs->GetCmdLineValue(PROFILE_SELECTION_CMD_LINE_ARG, getter_Copies(cmdResult)); if (NS_SUCCEEDED(rv)) { if (cmdResult) { profileURLStr = PROFILE_SELECTION_URL; } } // Start Profile Wizard rv = cmdLineArgs->GetCmdLineValue(PROFILE_WIZARD_CMD_LINE_ARG, getter_Copies(cmdResult)); if (NS_SUCCEEDED(rv)) { if (cmdResult) { profileURLStr = PROFILE_WIZARD_URL; } } // Start Migaration activity rv = cmdLineArgs->GetCmdLineValue(INSTALLER_CMD_LINE_ARG, getter_Copies(cmdResult)); if (NS_SUCCEEDED(rv)) { if (cmdResult) { 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 && numProfiles == 0) { PRBool confirmAutomigration = PR_FALSE; NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv) if (NS_SUCCEEDED(rv) && prefs) { rv = prefs->GetBoolPref(PREF_CONFIRM_AUTOMIGRATION, &confirmAutomigration); if (NS_FAILED(rv)) confirmAutomigration = PR_FALSE; } if (confirmAutomigration) { profileURLStr = CONFIRM_AUTOMIGRATE_URL; } else { AutoMigrate(); } } 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, nsFileSpec* profileDir) { NS_ENSURE_ARG_POINTER(profileName); NS_ENSURE_ARG_POINTER(profileDir); 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; nsCOMPtrspec; rv = NS_NewFileSpec(getter_AddRefs(spec)); if (NS_FAILED(rv)) return rv; nsCAutoString profileLocation; profileLocation.AssignWithConversion(aProfile->profileLocation); rv = spec->SetPersistentDescriptorString(profileLocation.GetBuffer()); if (NS_FAILED(rv)) return rv; rv = spec->GetFileSpec(profileDir); if (NS_FAILED(rv)) return rv; // Set this to be a current profile only if it is a 5.0 profile if (aProfile->isMigrated.EqualsWithConversion(REGISTRY_YES_STRING)) { gProfileDataAccess->SetCurrentProfile(profileName); nsFileSpec tmpFileSpec(*profileDir); PRBool inTrash = PR_FALSE; #ifdef XP_MAC nsSpecialSystemDirectory trashFolder(nsSpecialSystemDirectory::Mac_TrashDirectory); inTrash = tmpFileSpec.IsChildOf(trashFolder); #endif if (inTrash || !tmpFileSpec.Exists()) { // Get profile defaults folder.. NS_WITH_SERVICE(nsIFileLocator, locator, kFileLocatorCID, &rv); if (NS_FAILED(rv) || !locator) return NS_ERROR_FAILURE; nsCOMPtr profDefaultsDir; rv = locator->GetFileLocation( nsSpecialFileSpec::App_ProfileDefaultsFolder50, getter_AddRefs(profDefaultsDir)); if (NS_FAILED(rv) || !profDefaultsDir) return NS_ERROR_FAILURE; nsFileSpec defaultsDirSpec; profDefaultsDir->GetFileSpec(&defaultsDirSpec); #ifndef XP_MAC nsFilePath(tmpFileSpec.GetNativePathCString(), PR_TRUE); #else // Build new profile folder. Update Registry entries. // Return new folder. // Get the default location for user profiles nsCOMPtr defaultRoot; rv = locator->GetFileLocation( nsSpecialFileSpec::App_DefaultUserProfileRoot50, getter_AddRefs(defaultRoot)); if (NS_FAILED(rv) || !defaultRoot) return NS_ERROR_FAILURE; defaultRoot->GetFileSpec(&tmpFileSpec); if (!tmpFileSpec.Exists()) tmpFileSpec.CreateDirectory(); // append profile name tmpFileSpec += profileName; // Create New Directory. PersistentDescriptor needs an existing object. if (!tmpFileSpec.Exists()) tmpFileSpec.CreateDirectory(); // Get persistent string for profile directory nsXPIDLCString profileDirString; nsCOMPtrdirSpec; rv = NS_NewFileSpecWithSpec(tmpFileSpec, getter_AddRefs(dirSpec)); if (NS_SUCCEEDED(rv)) { rv = dirSpec->GetPersistentDescriptorString(getter_Copies(profileDirString)); } if (NS_FAILED(rv)) return rv; // Update profile struct entries with new value. nsAutoString profileLoc; profileLoc.AssignWithConversion(profileDirString); aProfile->profileLocation = profileLoc; gProfileDataAccess->SetValue(aProfile); // Return new file spec. nsCOMPtrnewSpec; rv = NS_NewFileSpec(getter_AddRefs(newSpec)); if (NS_FAILED(rv)) return rv; rv = newSpec->SetPersistentDescriptorString(profileDirString); if (NS_FAILED(rv)) return rv; rv = newSpec->GetFileSpec(profileDir); if (NS_FAILED(rv)) return rv; #endif // Copy contents from defaults folder. if (defaultsDirSpec.Exists()) defaultsDirSpec.RecursiveCopy(tmpFileSpec); } } delete aProfile; return rv; } NS_IMETHODIMP nsProfile::GetDefaultProfileParentDir(nsIFileSpec **aDefaultProfileDir) { NS_ENSURE_ARG_POINTER(aDefaultProfileDir); nsresult rv; NS_WITH_SERVICE(nsIFileLocator, locator, kFileLocatorCID, &rv); if (NS_FAILED(rv) || !locator) return NS_ERROR_FAILURE; rv = locator->GetFileLocation( nsSpecialFileSpec::App_DefaultUserProfileRoot50, aDefaultProfileDir); if (NS_FAILED(rv) || !aDefaultProfileDir || !*aDefaultProfileDir) return NS_ERROR_FAILURE; 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; } // Returns the name of the current profile i.e., the last used profile NS_IMETHODIMP nsProfile::GetCurrentProfile(PRUnichar **profileName) { NS_ENSURE_ARG_POINTER(profileName); gProfileDataAccess->GetCurrentProfile(profileName); return NS_OK; } // Returns the name of the current profile directory NS_IMETHODIMP nsProfile::GetCurrentProfileDir(nsFileSpec* profileDir) { NS_ENSURE_ARG_POINTER(profileDir); nsresult rv = NS_OK; nsXPIDLString profileName; GetCurrentProfile(getter_Copies(profileName)); rv = GetProfileDir(profileName, profileDir); if (NS_FAILED(rv)) return rv; return rv; } /* * Setters */ // Sets the current profile directory NS_IMETHODIMP nsProfile::SetProfileDir(const PRUnichar *profileName, nsFileSpec& profileDir) { NS_ENSURE_ARG_POINTER(profileName); nsresult rv = NS_OK; // Create a tmp Filespec and create a directory if required nsFileSpec tmpDir(profileDir); if (!profileDir.Exists()) { // nsPersistentFileDescriptor requires an existing // object. Make it first. tmpDir.CreateDirectory(); } nsXPIDLCString profileDirString; nsCOMPtrspec; rv = NS_NewFileSpecWithSpec(profileDir, getter_AddRefs(spec)); if (NS_SUCCEEDED(rv)) { rv = spec->GetPersistentDescriptorString(getter_Copies(profileDirString)); } if (NS_FAILED(rv)) return rv; // Do I need to check for NS_ERROR_OUT_OF_MEMORY when I do a new on a class? ProfileStruct* aProfile = new ProfileStruct(); nsAutoString profileLocation; profileLocation.AssignWithConversion(profileDirString); aProfile->profileName = profileName; aProfile->profileLocation = profileLocation; aProfile->isMigrated.AssignWithConversion(REGISTRY_YES_STRING); rv = CreateUserDirectories(tmpDir); if (NS_FAILED(rv)) { delete aProfile; return rv; } gProfileDataAccess->SetValue(aProfile); gProfileDataAccess->SetCurrentProfile(profileName); delete aProfile; return rv; } // Creates a new profile NS_IMETHODIMP nsProfile::CreateNewProfile(const PRUnichar* profileName, const PRUnichar* nativeProfileDir, const PRUnichar* langcode, PRBool useExistingDir) { NS_ENSURE_ARG_POINTER(profileName); nsresult rv = NS_OK; #if defined(DEBUG_profile) { printf("ProfileManager : CreateNewProfile\n"); nsCAutoString temp1; temp1.AssignWithConversion(profileName); printf("Profile Name: %s\n", NS_STATIC_CAST(const char*, temp1)); nsCAutoString temp2; temp2.AssignWithConversion(nativeProfileDir); printf("Profile Dir: %s\n", NS_STATIC_CAST(const char*, temp2)); } #endif NS_WITH_SERVICE(nsIFileLocator, locator, kFileLocatorCID, &rv); if (NS_FAILED(rv) || !locator) return NS_ERROR_FAILURE; if (!profileName) return NS_ERROR_FAILURE; nsFileSpec dirSpec; if (!nativeProfileDir || !*nativeProfileDir) { // They didn't specify a directory path... nsCOMPtr defaultRoot; rv = locator->GetFileLocation( nsSpecialFileSpec::App_DefaultUserProfileRoot50, getter_AddRefs(defaultRoot)); if (NS_FAILED(rv) || !defaultRoot) return NS_ERROR_FAILURE; defaultRoot->GetFileSpec(&dirSpec); if (!dirSpec.Exists()) dirSpec.CreateDirectory(); // append profile name dirSpec += profileName; // Make profile directory unique only when the user // decides to not use an already existing profile directory if (!useExistingDir) dirSpec.MakeUnique(); } else { dirSpec = nativeProfileDir; // this prevents people from choosing there profile directory // or another directory, and remove it when they delete the profile. // append profile name dirSpec += profileName; // Make profile directory unique only when the user // decides to not use an already existing profile directory if (!useExistingDir) dirSpec.MakeUnique(); } #if defined(DEBUG_profile_verbose) printf("before SetProfileDir\n"); #endif if (!dirSpec.Exists()) { dirSpec.CreateDirectory(); useExistingDir = PR_FALSE; } // Set the directory value and add the entry to the registry tree. // Creates required user directories. rv = SetProfileDir(profileName, dirSpec); #if defined(DEBUG_profile_verbose) printf("after SetProfileDir\n"); #endif // Get profile defaults folder.. nsCOMPtr profDefaultsDir; rv = locator->GetFileLocation( nsSpecialFileSpec::App_ProfileDefaultsFolder50_nloc, getter_AddRefs(profDefaultsDir)); if (NS_FAILED(rv) || !profDefaultsDir) { return NS_ERROR_FAILURE; } nsFileSpec defaultsDirSpec; profDefaultsDir->GetFileSpec(&defaultsDirSpec); if (langcode) { // caller prefers locale subdir nsFileSpec tmpdir; tmpdir = defaultsDirSpec; tmpdir += langcode; if (tmpdir.Exists()) defaultsDirSpec = tmpdir; } // Copy contents from defaults folder. if (defaultsDirSpec.Exists() && (!useExistingDir)) { defaultsDirSpec.RecursiveCopy(dirSpec); } gProfileDataAccess->mNumProfiles++; gProfileDataAccess->mProfileDataChanged = PR_TRUE; gProfileDataAccess->UpdateRegistry(); return NS_OK; } // Create required user directories like ImapMail, Mail, News, Cache etc. nsresult nsProfile::CreateUserDirectories(const nsFileSpec& profileDir) { nsresult rv = NS_OK; #if defined(DEBUG_profile_verbose) printf("ProfileManager : CreateUserDirectories\n"); #endif nsFileSpec tmpDir; tmpDir = profileDir; tmpDir += NEW_NEWS_DIR_NAME; if (!tmpDir.Exists()) tmpDir.CreateDirectory(); tmpDir = profileDir; tmpDir += NEW_IMAPMAIL_DIR_NAME; if (!tmpDir.Exists()) tmpDir.CreateDirectory(); tmpDir = profileDir; tmpDir += NEW_MAIL_DIR_NAME; if (!tmpDir.Exists()) tmpDir.CreateDirectory(); tmpDir = profileDir; tmpDir += "Cache"; if (!tmpDir.Exists()) tmpDir.CreateDirectory(); return rv; } // Delete all user directories associated with the a profile // A FileSpec of the profile's directory is taken as input param nsresult nsProfile::DeleteUserDirectories(const nsFileSpec& profileDir) { nsresult rv = NS_OK; #if defined(DEBUG_profile_verbose) printf("ProfileManager : DeleteUserDirectories\n"); #endif if (profileDir.Exists()) profileDir.Delete(PR_TRUE); return rv; } // 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_STATIC_CAST(const char*, temp1)); nsCAutoString temp2; temp2.AssignWithConversion(newName); printf("New name: %s\n", NS_STATIC_CAST(const char*, temp2)); } #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; } // 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 (NS_FAILED(rv)) return rv; /* profile is just replaced. But Keep up the count */ gProfileDataAccess->mNumProfiles++; rv = ForgetCurrentProfile(); if (NS_FAILED(rv)) return rv; gProfileDataAccess->mProfileDataChanged = PR_TRUE; gProfileDataAccess->UpdateRegistry(); 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; NS_WITH_SERVICE(nsIFileLocator, locator, kFileLocatorCID, &rv); if (NS_FAILED(rv)) return rv; if (!locator) return NS_ERROR_FAILURE; rv = locator->ForgetProfileDir(); if (NS_FAILED(rv)) return rv; 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 = NS_OK; rv = ForgetCurrentProfile(); if (NS_FAILED(rv)) return rv; // If user asks for it, delete profile directory if (canDeleteFiles) { nsFileSpec profileDirSpec; rv = GetProfileDir(profileName, &profileDirSpec); if (NS_FAILED(rv)) return rv; rv = DeleteUserDirectories(profileDirSpec); if (NS_FAILED(rv)) return rv; } // Remove the subtree from the registry. gProfileDataAccess->RemoveSubTree(profileName); if (NS_FAILED(rv)) return rv; gProfileDataAccess->mProfileDataChanged = PR_TRUE; gProfileDataAccess->UpdateRegistry(); return rv; } // Get the list of all profiles // Populate the input param. // This method is written to support the core service // call to get the names all profiles. NS_IMETHODIMP nsProfile::GetProfileList(PRUnichar **profileListStr) { NS_ENSURE_ARG_POINTER(profileListStr); gProfileDataAccess->GetProfileList(profileListStr); return NS_OK; } // launch the application with a profile of user's choice // Prefs and FileLocation services are used here. // FileLocation service to make ir forget about the global profile dir it had. // Prefs service to kick off the startup to start the app with new profile's prefs. NS_IMETHODIMP nsProfile::StartApprunner(const PRUnichar* profileName) { NS_ENSURE_ARG_POINTER(profileName); nsresult rv = NS_OK; #if defined(DEBUG_profile) { printf("ProfileManager : StartApprunner\n"); nsCAutoString temp; temp.AssignWithConversion(profileName); printf("profileName passed in: %s", NS_STATIC_CAST(const char*, temp)); } #endif gProfileDataAccess->SetCurrentProfile(profileName); mCurrentProfileAvailable = PR_TRUE; NS_WITH_SERVICE(nsIFileLocator, locator, kFileLocatorCID, &rv); if (NS_FAILED(rv)) return rv; if (!locator) return NS_ERROR_FAILURE; rv = locator->ForgetProfileDir(); if (NS_FAILED(rv)) { #ifdef DEBUG_profile printf("failed to forget the profile dir\n"); #endif /* DEBUG_profile */ return rv; } // Need to load new profile prefs. rv = LoadNewProfilePrefs(); return rv; } NS_IMETHODIMP nsProfile::LoadNewProfilePrefs() { nsresult rv; NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv); if (NS_FAILED(rv)) return rv; prefs->ResetPrefs(); prefs->ReadUserPrefs(); return NS_OK; } // Migrate profile information from the 4x registry to 5x registry. NS_IMETHODIMP nsProfile::MigrateProfileInfo() { nsresult rv = NS_OK; #if defined(XP_PC) || defined(XP_MAC) #if defined(DEBUG_profile_verbose) printf("Entered MigrateProfileInfo.\n"); #endif char oldRegFile[_MAX_LENGTH] = {'\0'}; #ifdef XP_PC // Registry file has been traditionally stored in the windows directory (XP_PC). nsSpecialSystemDirectory systemDir(nsSpecialSystemDirectory::Win_WindowsDirectory); // Append the name of the old registry to the path obtained. PL_strcpy(oldRegFile, systemDir.GetNativePathCString()); PL_strcat(oldRegFile, OLD_REGISTRY_FILE_NAME); #else /* XP_MAC */ nsSpecialSystemDirectory regLocation(nsSpecialSystemDirectory::Mac_SystemDirectory); // Append the name of the old registry to the path obtained. regLocation += "Preferences"; regLocation += OLD_REGISTRY_FILE_NAME; PL_strcpy(oldRegFile, regLocation.GetNativePathCString()); #endif /* XP_PC */ rv = gProfileDataAccess->Get4xProfileInfo(oldRegFile); #elif defined (XP_BEOS) #else /* XP_UNIX */ rv = gProfileDataAccess->Get4xProfileInfo(nsnull); #endif /* XP_PC || XP_MAC */ gProfileDataAccess->mProfileDataChanged = PR_TRUE; gProfileDataAccess->UpdateRegistry(); 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 NS_IMETHODIMP nsProfile::MigrateProfile(const PRUnichar* profileName, PRBool showProgressAsModalWindow) { NS_ENSURE_ARG_POINTER(profileName); nsresult rv = NS_OK; #if defined(DEBUG_profile) printf("Inside Migrate Profile routine.\n" ); #endif nsFileSpec oldProfDir; nsFileSpec newProfDir; rv = GetProfileDir(profileName, &oldProfDir); if (NS_FAILED(rv)) return rv; // Create new profile dir path NS_WITH_SERVICE(nsIFileLocator, locator, kFileLocatorCID, &rv); if (NS_FAILED(rv) || !locator) return NS_ERROR_FAILURE; // Get current profile, make the new one a sibling... nsCOMPtr newSpec; rv = locator->GetFileLocation( nsSpecialFileSpec::App_DefaultUserProfileRoot50, getter_AddRefs(newSpec)); if (!newSpec) return NS_ERROR_FAILURE; newSpec->GetFileSpec(&newProfDir); newProfDir += profileName; newProfDir.MakeUnique(); if (newProfDir.Exists()) { #ifdef DEBUG_profile printf("directory already exists\n"); #endif return NS_ERROR_FAILURE; } // Call migration service to do the work. nsCOMPtr pPrefMigrator; rv = nsComponentManager::CreateInstance(kPrefMigrationCID, nsnull, NS_GET_IID(nsIPrefMigration), getter_AddRefs(pPrefMigrator)); if (NS_FAILED(rv)) return rv; if (!pPrefMigrator) return NS_ERROR_FAILURE; nsXPIDLCString oldProfDirStr; nsXPIDLCString newProfDirStr; if (!newProfDir.Exists()) { newProfDir.CreateDirectory(); } rv = GetStringFromSpec(newProfDir, getter_Copies(newProfDirStr)); if (NS_FAILED(rv)) return rv; if (!oldProfDir.Exists()) { return NS_ERROR_FAILURE; } rv = GetStringFromSpec(oldProfDir, getter_Copies(oldProfDirStr)); if (NS_FAILED(rv)) return rv; // you can do this a bunch of times. rv = pPrefMigrator->AddProfilePaths(oldProfDirStr, newProfDirStr); rv = pPrefMigrator->ProcessPrefs(showProgressAsModalWindow); if (NS_FAILED(rv)) return rv; // Copy the default 5.0 profile files into the migrated profile // Get profile defaults folder.. nsCOMPtr profDefaultsDir; rv = locator->GetFileLocation( nsSpecialFileSpec::App_ProfileDefaultsFolder50, getter_AddRefs(profDefaultsDir)); if (NS_FAILED(rv) || !profDefaultsDir) { return NS_ERROR_FAILURE; } // Copy panels.rdf file // This is a hack. Once the localFileSpec implementation // is complete, this will be removed. nsFileSpec defaultsDirSpecFile; profDefaultsDir->GetFileSpec(&defaultsDirSpecFile); defaultsDirSpecFile += PANELS_RDF_FILE; if (defaultsDirSpecFile.Exists()) { defaultsDirSpecFile.CopyToDir(newProfDir); } // hack finish. rv = SetProfileDir(profileName, newProfDir); if (NS_FAILED(rv)) return rv; gProfileDataAccess->mNumProfiles++; gProfileDataAccess->mNumOldProfiles--; gProfileDataAccess->mProfileDataChanged = PR_TRUE; gProfileDataAccess->UpdateRegistry(); 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 = NS_OK; for (PRInt32 i=0; i < gProfileDataAccess->mNumOldProfiles; i++) { ProfileStruct* profileItem = (ProfileStruct *) (gProfileDataAccess->m4xProfiles->ElementAt(i)); rv = MigrateProfile(profileItem->profileName.GetUnicode(), PR_FALSE /* don't show progress as modal window */); if (NS_FAILED(rv)) return rv; } return rv; } nsresult nsProfile::RenameProfileDir(const PRUnichar* newProfileName) { NS_ASSERTION(newProfileName, "Invalid new profile name"); nsresult rv = NS_OK; nsFileSpec dirSpec; rv = GetProfileDir(newProfileName, &dirSpec); if (NS_FAILED(rv)) return rv; nsFileSpec renamedDirSpec = dirSpec; renamedDirSpec.SetLeafName(newProfileName); renamedDirSpec.MakeUnique(); // rename the directory rv = dirSpec.Rename(renamedDirSpec.GetLeafName()); if (NS_FAILED(rv)) return rv; // update the registry rv = SetProfileDir(newProfileName, dirSpec); if (NS_FAILED(rv)) return rv; return NS_OK; } 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 nsFileSpec currProfileDir; nsFileSpec newProfileDir; NS_WITH_SERVICE(nsIFileLocator, locator, kFileLocatorCID, &rv); if (NS_FAILED(rv) || !locator) return NS_ERROR_FAILURE; GetCurrentProfileDir(&currProfileDir); if (currProfileDir.Exists()) { nsCOMPtr dirSpec; rv = locator->GetFileLocation( nsSpecialFileSpec::App_DefaultUserProfileRoot50, getter_AddRefs(dirSpec)); if (NS_FAILED(rv) || !dirSpec) return NS_ERROR_FAILURE; //Append profile name to form a directory name dirSpec->GetFileSpec(&newProfileDir); // TODO: // hash profileName (will MakeUnique do that for us?) // don't allow special characters (like ..) // make acceptable length (will MakeUnique do that for us?) newProfileDir += newProfile; newProfileDir.MakeUnique(); if (newProfileDir.Exists()) { #ifdef DEBUG_profile printf("directory already exists\n"); #endif return NS_ERROR_FAILURE; } currProfileDir.RecursiveCopy(newProfileDir); rv = SetProfileDir(newProfile, newProfileDir); } #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", NS_STATIC_CAST(const char*, temp)); } #endif gProfileDataAccess->mNumProfiles++; gProfileDataAccess->mProfileDataChanged = PR_TRUE; rv = locator->ForgetProfileDir(); if (NS_FAILED(rv)) return rv; return rv; } nsresult nsProfile::CreateDefaultProfile(void) { nsresult rv = NS_OK; nsFileSpec profileDirSpec; // Get the default user profiles folder NS_WITH_SERVICE(nsIFileLocator, locator, kFileLocatorCID, &rv); if (NS_FAILED(rv) || !locator) return NS_ERROR_FAILURE; nsCOMPtr spec; rv = locator->GetFileLocation( nsSpecialFileSpec::App_DefaultUserProfileRoot50, getter_AddRefs(spec)); if (NS_FAILED(rv) || !spec) return NS_ERROR_FAILURE; spec->GetFileSpec(&profileDirSpec); rv = locator->ForgetProfileDir(); nsAutoString dirSpecStr; dirSpecStr.AssignWithConversion(profileDirSpec.GetNativePathCString()); nsAutoString defaultProfileName; defaultProfileName.AssignWithConversion(DEFAULT_PROFILE_NAME); rv = CreateNewProfile(defaultProfileName.GetUnicode(), dirSpecStr.GetUnicode(), nsnull, PR_TRUE); return rv; } NS_IMETHODIMP nsProfile::UpdateRegistry(void) { nsresult rv = NS_OK; gProfileDataAccess->mProfileDataChanged = PR_TRUE; rv= gProfileDataAccess->UpdateRegistry(); 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; 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; }