/* -*- 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): * Pierre Phaneuf * * 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 "nsProfileAccess.h" #include "nsProfile.h" #include "pratom.h" #include "prmem.h" #include "plstr.h" #include "prenv.h" #include "nsIEnumerator.h" #include "prprf.h" #include "nsCOMPtr.h" #include "nsIComponentManager.h" #include "nsEscape.h" #include "nsDirectoryServiceDefs.h" #include "nsAppDirectoryServiceDefs.h" #include "nsILocalFile.h" #include "nsReadableUtils.h" #include "nsNativeCharsetUtils.h" #if defined(XP_MAC) || defined(XP_MACOSX) #include #include #include "nsILocalFileMac.h" #endif #ifdef XP_UNIX #include #include #include #include #include "prnetdb.h" #include "prsystem.h" #endif #ifdef VMS #include #endif #if defined (XP_UNIX) #define USER_ENVIRONMENT_VARIABLE "USER" #define LOGNAME_ENVIRONMENT_VARIABLE "LOGNAME" #define HOME_ENVIRONMENT_VARIABLE "HOME" #define PROFILE_NAME_ENVIRONMENT_VARIABLE "PROFILE_NAME" #define PROFILE_HOME_ENVIRONMENT_VARIABLE "PROFILE_HOME" #define DEFAULT_UNIX_PROFILE_NAME "default" #ifndef XP_MACOSX /* Don't use symlink-based locking on OS X */ #define USE_SYMLINK_LOCKING #endif #elif defined (XP_BEOS) #endif // Registry Keys #define kRegistryYesString (NS_LITERAL_STRING("yes")) #define kRegistryNoString (NS_LITERAL_STRING("no")) #define kRegistryProfileSubtreeString (NS_LITERAL_STRING("Profiles")) #define kRegistryCurrentProfileString (NS_LITERAL_STRING("CurrentProfile")) #define kRegistryNCServiceDenialString (NS_LITERAL_STRING("NCServiceDenial")) #define kRegistryNCProfileNameString (NS_LITERAL_STRING("NCProfileName")) #define kRegistryNCUserEmailString (NS_LITERAL_STRING("NCEmailAddress")) #define kRegistryNCHavePREGInfoString (NS_LITERAL_STRING("NCHavePregInfo")) #define kRegistryHavePREGInfoString (NS_LITERAL_STRING("HavePregInfo")) #define kRegistryMigratedString (NS_LITERAL_STRING("migrated")) #define kRegistryDirectoryString (NS_LITERAL_STRING("directory")) #define kRegistryNeedMigrationString (NS_LITERAL_STRING("NeedMigration")) #define kRegistryMozRegDataMovedString (NS_LITERAL_STRING("OldRegDataMoved")) #define kRegistryCreationTimeString (NS_LITERAL_CSTRING("CreationTime")) #define kRegistryLastModTimeString (NS_LITERAL_CSTRING("LastModTime")) #define kRegistryMigratedFromString (NS_LITERAL_CSTRING("MigFromDir")) #define kRegistryVersionString (NS_LITERAL_STRING("Version")) #define kRegistryVersion_1_0 (NS_LITERAL_STRING("1.0")) #define kRegistryCurrentVersion (NS_LITERAL_STRING("1.0")) #define kRegistryStartWithLastString (NS_LITERAL_CSTRING("AutoStartWithLast")) // ********************************************************************** // class nsProfileAccess // ********************************************************************** /* * Constructor/Destructor * FillProfileInfo reads the registry and fills profileStructs */ nsProfileAccess::nsProfileAccess() { mProfileDataChanged = PR_FALSE; mForgetProfileCalled = PR_FALSE; m4xProfilesAdded = PR_FALSE; mStartWithLastProfile = PR_FALSE; mProfiles = new nsVoidArray(); // Get the profile registry path NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_FILE, getter_AddRefs(mNewRegFile)); // Read the data into internal data structure.... FillProfileInfo(mNewRegFile); } // On the way out, close the registry if it is // still opened and free up the resources. nsProfileAccess::~nsProfileAccess() { // Release all resources. mNewRegFile = nsnull; FreeProfileMembers(mProfiles); } // Free up the member profile structs void nsProfileAccess::FreeProfileMembers(nsVoidArray *profiles) { NS_ASSERTION(profiles, "Invalid profiles"); PRInt32 index = 0; PRInt32 numElems = profiles->Count(); ProfileStruct* aProfile; if (profiles) { for (index = 0; index < numElems; index++) { aProfile = (ProfileStruct *) profiles->ElementAt(index); delete aProfile; } delete profiles; } } // Given the name of the profile, the structure that // contains the relavant profile information will be filled. // Caller must free up the profile struct. nsresult nsProfileAccess::GetValue(const PRUnichar* profileName, ProfileStruct** aProfile) { NS_ENSURE_ARG(profileName); NS_ENSURE_ARG_POINTER(aProfile); *aProfile = nsnull; PRInt32 index = 0; index = FindProfileIndex(profileName, PR_FALSE); if (index < 0) return NS_ERROR_FAILURE; ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); *aProfile = new ProfileStruct(*profileItem); if (!*aProfile) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } // This method writes all changes to the array of the // profile structs. If it is an existing profile, it // will be updated. If it is a new profile, it gets added // to the list. nsresult nsProfileAccess::SetValue(ProfileStruct* aProfile) { NS_ENSURE_ARG(aProfile); PRInt32 index = 0; ProfileStruct* profileItem; index = FindProfileIndex(aProfile->profileName.get(), aProfile->isImportType); if (index >= 0) { profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); *profileItem = *aProfile; profileItem->updateProfileEntry = PR_TRUE; } else { profileItem = new ProfileStruct(*aProfile); if (!profileItem) return NS_ERROR_OUT_OF_MEMORY; profileItem->updateProfileEntry = PR_TRUE; if (!mProfiles) { mProfiles = new nsVoidArray; if (!mProfiles) return NS_ERROR_OUT_OF_MEMORY; } mProfiles->AppendElement((void*)profileItem); } return NS_OK; } // Enumerates through the registry for profile // information. Reads in the data into the array // of profile structs. After this, all the callers // requesting profile info will get thier data from // profiles array. All the udates will be done to this // data structure to reflect the latest status. // Data will be flushed at the end. nsresult nsProfileAccess::FillProfileInfo(nsIFile* regName) { nsresult rv = NS_OK; nsCOMPtr registry(do_CreateInstance(NS_REGISTRY_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; rv = registry->Open(regName); if (NS_FAILED(rv)) return rv; // Enumerate all subkeys (immediately) under the given node. nsCOMPtr enumKeys; nsRegistryKey profilesTreeKey; rv = registry->GetKey(nsIRegistry::Common, kRegistryProfileSubtreeString.get(), &profilesTreeKey); if (NS_FAILED(rv)) { rv = registry->AddKey(nsIRegistry::Common, kRegistryProfileSubtreeString.get(), &profilesTreeKey); if (NS_FAILED(rv)) return rv; } // introducing these tmp variables as nsString variables cannot be passed to // the resgitry methods nsXPIDLString tmpCurrentProfile; nsXPIDLString tmpVersion; nsXPIDLString tmpPREGInfo; // For the following variables, we do not check for the rv value // but check for the variable instead, because it is valid to proceed // without the variables having a value. That's why there are no returns // for invalid rv values. // Get the current profile rv = registry->GetString(profilesTreeKey, kRegistryCurrentProfileString.get(), getter_Copies(tmpCurrentProfile)); if (tmpCurrentProfile) { // If current profile does not exist, mCurrentProfile will not be set // This is not harmful, as GetCurrentProfile method needs to return this value // And GetCurrentProfile returns: // the current profile if set // the first profile if profiles exist but no current profile is set // an empty string if no profiles exist. mCurrentProfile = NS_STATIC_CAST(const PRUnichar*, tmpCurrentProfile); } // Get the profile version rv = registry->GetString(profilesTreeKey, kRegistryVersionString.get(), getter_Copies(tmpVersion)); // Get the preg info rv = registry->GetString(profilesTreeKey, kRegistryHavePREGInfoString.get(), getter_Copies(tmpPREGInfo)); if (tmpPREGInfo == nsnull) { mHavePREGInfo = kRegistryNoString; mProfileDataChanged = PR_TRUE; } // Get the StartWithLastProfile flag PRInt32 tempLong; rv = registry->GetInt(profilesTreeKey, kRegistryStartWithLastString.get(), &tempLong); if (NS_SUCCEEDED(rv)) mStartWithLastProfile = tempLong; rv = registry->EnumerateSubtrees( profilesTreeKey, getter_AddRefs(enumKeys)); if (NS_FAILED(rv)) return rv; rv = enumKeys->First(); if (NS_FAILED(rv)) return rv; PRBool currentProfileValid = mCurrentProfile.IsEmpty(); while (NS_OK != enumKeys->IsDone()) { nsCOMPtr base; rv = enumKeys->CurrentItem( getter_AddRefs(base) ); if (NS_FAILED(rv)) return rv; // Get specific interface. nsCOMPtr node; nsIID nodeIID = NS_IREGISTRYNODE_IID; rv = base->QueryInterface( nodeIID, getter_AddRefs(node)); if (NS_FAILED(rv)) return rv; // Get node name. nsXPIDLString profile; nsXPIDLString isMigrated; nsXPIDLString NCProfileName; nsXPIDLString NCDeniedService; nsXPIDLString NCEmailAddress; nsXPIDLString NCHavePregInfo; rv = node->GetName(getter_Copies(profile)); if (NS_FAILED(rv)) return rv; nsRegistryKey profKey; rv = node->GetKey(&profKey); if (NS_FAILED(rv)) return rv; rv = registry->GetString(profKey, kRegistryMigratedString.get(), getter_Copies(isMigrated)); if (NS_FAILED(rv)) return rv; nsDependentString isMigratedString(isMigrated); // Not checking the return values of these variables as they // are for activation, they are optional and their values // do not call for a return registry->GetString(profKey, kRegistryNCProfileNameString.get(), getter_Copies(NCProfileName)); registry->GetString(profKey, kRegistryNCServiceDenialString.get(), getter_Copies(NCDeniedService)); registry->GetString(profKey, kRegistryNCUserEmailString.get(), getter_Copies(NCEmailAddress)); registry->GetString(profKey, kRegistryNCHavePREGInfoString.get(), getter_Copies(NCHavePregInfo)); // Make sure that mCurrentProfile is valid if (!mCurrentProfile.IsEmpty() && mCurrentProfile.Equals(profile)) currentProfileValid = PR_TRUE; ProfileStruct* profileItem = new ProfileStruct(); if (!profileItem) return NS_ERROR_OUT_OF_MEMORY; profileItem->updateProfileEntry = PR_TRUE; profileItem->profileName = NS_STATIC_CAST(const PRUnichar*, profile); PRInt64 tmpLongLong; rv = registry->GetLongLong(profKey, kRegistryCreationTimeString.get(), &tmpLongLong); if (NS_SUCCEEDED(rv)) profileItem->creationTime = tmpLongLong; rv = registry->GetLongLong(profKey, kRegistryLastModTimeString.get(), &tmpLongLong); if (NS_SUCCEEDED(rv)) profileItem->lastModTime = tmpLongLong; rv = profileItem->InternalizeLocation(registry, profKey, PR_FALSE); NS_ASSERTION(NS_SUCCEEDED(rv), "Internalizing profile location failed"); // Not checking the error since most won't have this info profileItem->InternalizeMigratedFromLocation(registry, profKey); profileItem->isMigrated = isMigratedString.Equals(kRegistryYesString); if (NCProfileName) profileItem->NCProfileName = NS_STATIC_CAST(const PRUnichar*, NCProfileName); if (NCDeniedService) profileItem->NCDeniedService = NS_STATIC_CAST(const PRUnichar*, NCDeniedService); if (NCEmailAddress) profileItem->NCEmailAddress = NS_STATIC_CAST(const PRUnichar*, NCEmailAddress); if (NCHavePregInfo) profileItem->NCHavePregInfo = NS_STATIC_CAST(const PRUnichar*, NCHavePregInfo); profileItem->isImportType = PR_FALSE; if (!mProfiles) { mProfiles = new nsVoidArray(); if (!mProfiles) { delete profileItem; return NS_ERROR_OUT_OF_MEMORY; } } mProfiles->AppendElement((void*)profileItem); rv = enumKeys->Next(); if (NS_FAILED(rv)) return rv; } if (!currentProfileValid) mCurrentProfile.SetLength(0); return rv; } // Return the number of 5x profiles. void nsProfileAccess::GetNumProfiles(PRInt32 *numProfiles) { if (!numProfiles) { NS_ASSERTION(PR_FALSE, "invalid argument"); return; } PRInt32 index, numElems = mProfiles->Count(); *numProfiles = 0; for(index = 0; index < numElems; index++) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); if (profileItem->isMigrated && !profileItem->isImportType) { (*numProfiles)++; } } } // Return the number of 4x (>=4.5 & < 5.0) profiles. void nsProfileAccess::GetNum4xProfiles(PRInt32 *numProfiles) { if (!numProfiles) { NS_ASSERTION(PR_FALSE, "invalid argument"); return; } PRInt32 index, numElems = mProfiles->Count(); *numProfiles = 0; for(index = 0; index < numElems; index++) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); if (!profileItem->isMigrated && !profileItem->isImportType) { (*numProfiles)++; } } } // If the application can't find the current profile, // the first profile will be used as the current profile. // This routine returns the first 5x profile. // Caller must free up the string (firstProfile). void nsProfileAccess::GetFirstProfile(PRUnichar **firstProfile) { if (!firstProfile) { NS_ASSERTION(PR_FALSE, "Invalid firstProfile pointer"); return; } PRInt32 index, numElems = mProfiles->Count(); *firstProfile = nsnull; for(index = 0; index < numElems; index++) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); if (profileItem->isMigrated && !profileItem->isImportType) { *firstProfile = ToNewUnicode(profileItem->profileName); break; } } } // Set the current profile. Opearting directly on the tree. // A separate struct should be maintained for the top level info. // That way we can eliminate additional registry access. For // now, we depend on registry operations. // Capture the current profile information into mCurrentProfile. void nsProfileAccess::SetCurrentProfile(const PRUnichar *profileName) { NS_ASSERTION(profileName, "Invalid profile name"); mCurrentProfile = profileName; mProfileDataChanged = PR_TRUE; } // Return the current profile value. // If mCurrent profile is already set, that value is returned. // If there is only one profile that value is set to CurrentProfile. void nsProfileAccess::GetCurrentProfile(PRUnichar **profileName) { *profileName = nsnull; if (!mCurrentProfile.IsEmpty() || mForgetProfileCalled) { *profileName = ToNewUnicode(mCurrentProfile); } // If there are profiles and profileName is not // set yet. Get the first one and set it as Current Profile. if (*profileName == nsnull) { GetFirstProfile(profileName); // We might not have any if (*profileName) SetCurrentProfile(*profileName); } } // Delete a profile from profile structs void nsProfileAccess::RemoveSubTree(const PRUnichar* profileName) { NS_ASSERTION(profileName, "Invalid profile name"); // delete this entry from the mProfiles array PRInt32 index = FindProfileIndex(profileName, PR_FALSE); if (index >= 0) { mProfiles->RemoveElementAt(index); if (mCurrentProfile.Equals(profileName)) { mCurrentProfile.SetLength(0); } } } // Return the index of a given profiel from the arraf of profile structs. PRInt32 nsProfileAccess::FindProfileIndex(const PRUnichar* profileName, PRBool forImport) { NS_ASSERTION(profileName, "Invalid profile name"); PRInt32 retval = -1; PRInt32 index, numElems = mProfiles->Count(); for (index=0; index < numElems; index++) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); if(profileItem->profileName.Equals(profileName) && (profileItem->isImportType == forImport)) { retval = index; break; } } return retval; } // Flush profile information from the data structure to the registry. nsresult nsProfileAccess::UpdateRegistry(nsIFile* regName) { nsresult rv; if (!mProfileDataChanged) { return NS_OK; } if (!regName) { regName = mNewRegFile; } nsCOMPtr registry(do_CreateInstance(NS_REGISTRY_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; rv = registry->Open(regName); if (NS_FAILED(rv)) return rv; // Enumerate all subkeys (immediately) under the given node. nsCOMPtr enumKeys; nsRegistryKey profilesTreeKey; // Get the major subtree rv = registry->GetKey(nsIRegistry::Common, kRegistryProfileSubtreeString.get(), &profilesTreeKey); if (NS_FAILED(rv)) { rv = registry->AddKey(nsIRegistry::Common, kRegistryProfileSubtreeString.get(), &profilesTreeKey); if (NS_FAILED(rv)) return rv; } // Set the current profile if (!mCurrentProfile.IsEmpty()) { rv = registry->SetString(profilesTreeKey, kRegistryCurrentProfileString.get(), mCurrentProfile.get()); if (NS_FAILED(rv)) return rv; } // Set the registry version rv = registry->SetString(profilesTreeKey, kRegistryVersionString.get(), kRegistryCurrentVersion.get()); if (NS_FAILED(rv)) return rv; // Set preg info rv = registry->SetString(profilesTreeKey, kRegistryHavePREGInfoString.get(), mHavePREGInfo.get()); if (NS_FAILED(rv)) return rv; // Set the StartWithLastProfile flag rv = registry->SetInt(profilesTreeKey, kRegistryStartWithLastString.get(), mStartWithLastProfile); if (NS_FAILED(rv)) return rv; rv = registry->EnumerateSubtrees(profilesTreeKey, getter_AddRefs(enumKeys)); if (NS_FAILED(rv)) return rv; rv = enumKeys->First(); if (NS_FAILED(rv)) return rv; while (NS_OK != enumKeys->IsDone()) { nsCOMPtr base; rv = enumKeys->CurrentItem( getter_AddRefs(base) ); if (NS_FAILED(rv)) return rv; // Get specific interface. nsCOMPtr node; nsIID nodeIID = NS_IREGISTRYNODE_IID; rv = base->QueryInterface( nodeIID, getter_AddRefs(node)); if (NS_FAILED(rv)) return rv; // Get node name. nsXPIDLString profile; nsXPIDLString isMigrated; nsXPIDLString directory; rv = node->GetName( getter_Copies(profile) ); if (NS_FAILED(rv)) return rv; PRInt32 index = 0; index = FindProfileIndex(profile, PR_FALSE); if (index < 0) { // This profile is deleted. rv = registry->RemoveKey(profilesTreeKey, profile); if (NS_FAILED(rv)) return rv; } else { nsRegistryKey profKey; ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); rv = node->GetKey(&profKey); if (NS_FAILED(rv)) return rv; rv = registry->SetString(profKey, kRegistryMigratedString.get(), profileItem->isMigrated ? kRegistryYesString.get() : kRegistryNoString.get()); if (NS_FAILED(rv)) return rv; registry->SetString(profKey, kRegistryNCProfileNameString.get(), profileItem->NCProfileName.get()); registry->SetString(profKey, kRegistryNCServiceDenialString.get(), profileItem->NCDeniedService.get()); registry->SetString(profKey, kRegistryNCUserEmailString.get(), profileItem->NCEmailAddress.get()); registry->SetString(profKey, kRegistryNCHavePREGInfoString.get(), profileItem->NCHavePregInfo.get()); registry->SetLongLong(profKey, kRegistryCreationTimeString.get(), &profileItem->creationTime); registry->SetLongLong(profKey, kRegistryLastModTimeString.get(), &profileItem->lastModTime); rv = profileItem->ExternalizeLocation(registry, profKey); if (NS_FAILED(rv)) { NS_ASSERTION(PR_FALSE, "Could not update profile location"); rv = enumKeys->Next(); if (NS_FAILED(rv)) return rv; continue; } profileItem->ExternalizeMigratedFromLocation(registry, profKey); profileItem->updateProfileEntry = PR_FALSE; } rv = enumKeys->Next(); if (NS_FAILED(rv)) return rv; } // Take care of new nodes PRInt32 numElems = mProfiles->Count(); for (int i = 0; i < numElems; i++) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(i)); if (!profileItem->isImportType && profileItem->updateProfileEntry) { nsRegistryKey profKey; rv = registry->AddKey(profilesTreeKey, profileItem->profileName.get(), &profKey); if (NS_FAILED(rv)) return rv; rv = registry->SetString(profKey, kRegistryMigratedString.get(), profileItem->isMigrated ? kRegistryYesString.get() : kRegistryNoString.get()); if (NS_FAILED(rv)) return rv; registry->SetString(profKey, kRegistryNCProfileNameString.get(), profileItem->NCProfileName.get()); registry->SetString(profKey, kRegistryNCServiceDenialString.get(), profileItem->NCDeniedService.get()); registry->SetString(profKey, kRegistryNCUserEmailString.get(), profileItem->NCEmailAddress.get()); registry->SetString(profKey, kRegistryNCHavePREGInfoString.get(), profileItem->NCHavePregInfo.get()); registry->SetLongLong(profKey, kRegistryCreationTimeString.get(), &profileItem->creationTime); registry->SetLongLong(profKey, kRegistryLastModTimeString.get(), &profileItem->lastModTime); rv = profileItem->ExternalizeLocation(registry, profKey); if (NS_FAILED(rv)) { NS_ASSERTION(PR_FALSE, "Could not update profile location"); continue; } profileItem->ExternalizeMigratedFromLocation(registry, profKey); profileItem->updateProfileEntry = PR_FALSE; } } mProfileDataChanged = PR_FALSE; return rv; } nsresult nsProfileAccess::GetOriginalProfileDir(const PRUnichar *profileName, nsILocalFile **originalDir) { NS_ENSURE_ARG(profileName); NS_ENSURE_ARG_POINTER(originalDir); *originalDir = nsnull; nsresult rv = NS_OK; PRInt32 index = FindProfileIndex(profileName, PR_TRUE); if (index >= 0) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); nsCOMPtr profileDir; rv = profileItem->GetResolvedProfileDir(getter_AddRefs(profileDir)); if (NS_SUCCEEDED(rv) && profileDir) { #ifdef XP_MAC PRBool exists; rv = profileDir->Exists(&exists); if (NS_FAILED(rv)) return rv; if (exists) { PRBool inTrash; nsCOMPtr trashFolder; rv = NS_GetSpecialDirectory(NS_MAC_TRASH_DIR, getter_AddRefs(trashFolder)); if (NS_FAILED(rv)) return rv; rv = trashFolder->Contains(profileDir, PR_TRUE, &inTrash); if (NS_FAILED(rv)) return rv; if (inTrash) { return NS_ERROR_FILE_NOT_FOUND; } } #endif NS_IF_ADDREF(*originalDir = profileDir); } return rv; } return NS_ERROR_FAILURE; } nsresult nsProfileAccess::SetMigratedFromDir(const PRUnichar *profileName, nsILocalFile *originalDir) { NS_ENSURE_ARG(profileName); NS_ENSURE_ARG(originalDir); PRInt32 index = FindProfileIndex(profileName, PR_FALSE); if (index >= 0) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); profileItem->migratedFrom = originalDir; profileItem->updateProfileEntry = PR_TRUE; return NS_OK; } return NS_ERROR_FAILURE; } nsresult nsProfileAccess::SetProfileLastModTime(const PRUnichar *profileName, PRInt64 lastModTime) { NS_ENSURE_ARG(profileName); PRInt32 index = FindProfileIndex(profileName, PR_FALSE); if (index >= 0) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); profileItem->lastModTime = lastModTime; profileItem->updateProfileEntry = PR_TRUE; return NS_OK; } return NS_ERROR_FAILURE; } nsresult nsProfileAccess::GetStartWithLastUsedProfile(PRBool *aStartWithLastUsedProfile) { NS_ENSURE_ARG_POINTER(aStartWithLastUsedProfile); *aStartWithLastUsedProfile = mStartWithLastProfile; return NS_OK; } nsresult nsProfileAccess::SetStartWithLastUsedProfile(PRBool aStartWithLastUsedProfile) { mStartWithLastProfile = aStartWithLastUsedProfile; mProfileDataChanged = PR_TRUE; return NS_OK; } // Return the list of profiles, 4x, 5x, or both. nsresult nsProfileAccess::GetProfileList(PRInt32 whichKind, PRUint32 *length, PRUnichar ***result) { NS_ENSURE_ARG_POINTER(length); *length = 0; NS_ENSURE_ARG_POINTER(result); *result = nsnull; nsresult rv = NS_OK; PRInt32 count, localLength = 0; PRUnichar **outArray, **next; PRInt32 numElems = mProfiles->Count(); PRInt32 profilesCount; switch (whichKind) { case nsIProfileInternal::LIST_ONLY_NEW: GetNumProfiles(&count); break; case nsIProfileInternal::LIST_ONLY_OLD: GetNum4xProfiles(&count); break; case nsIProfileInternal::LIST_ALL: GetNum4xProfiles(&count); GetNumProfiles(&profilesCount); count += profilesCount; break; case nsIProfileInternal::LIST_FOR_IMPORT: GetNum4xProfiles(&count); GetNumProfiles(&profilesCount); count = numElems - (count + profilesCount); break; default: NS_ASSERTION(PR_FALSE, "Bad parameter"); return NS_ERROR_INVALID_ARG; } next = outArray = (PRUnichar **)nsMemory::Alloc(count * sizeof(PRUnichar *)); if (!outArray) return NS_ERROR_OUT_OF_MEMORY; for (PRInt32 index=0; index < numElems && localLength < count; index++) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); if (whichKind == nsIProfileInternal::LIST_ONLY_OLD && (profileItem->isMigrated || profileItem->isImportType)) continue; else if (whichKind == nsIProfileInternal::LIST_ONLY_NEW && (!profileItem->isMigrated || profileItem->isImportType)) continue; else if (whichKind == nsIProfileInternal::LIST_ALL && profileItem->isImportType) continue; else if (whichKind == nsIProfileInternal::LIST_FOR_IMPORT && !profileItem->isImportType) continue; *next = ToNewUnicode(profileItem->profileName); if (*next == nsnull) { rv = NS_ERROR_OUT_OF_MEMORY; break; } next++; localLength++; } if (NS_SUCCEEDED(rv)) { *result = outArray; *length = localLength; } else { while (--next >= outArray) nsMemory::Free(*next); nsMemory::Free(outArray); } return rv; } // Return a boolean based on the profile existence. PRBool nsProfileAccess::ProfileExists(const PRUnichar *profileName) { NS_ASSERTION(profileName, "Invalid profile name"); PRBool exists = PR_FALSE; PRInt32 numElems = mProfiles->Count(); for (PRInt32 index=0; index < numElems; index++) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); if (!profileItem->isImportType && profileItem->profileName.Equals(profileName)) { exists = PR_TRUE; break; } } return exists; } // Capture the 4x profile information from the old registry (4x) nsresult nsProfileAccess::Get4xProfileInfo(nsIFile *registryFile, PRBool fromImport) { nsresult rv = NS_OK; if (fromImport && m4xProfilesAdded) return rv; #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_MAC) || defined(XP_MACOSX) NS_ENSURE_ARG(registryFile); nsCOMPtr oldReg(do_CreateInstance(NS_REGISTRY_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; rv = oldReg->Open(registryFile); if (NS_FAILED(rv)) return rv; // Enumerate 4x tree and create an array of that information. // Enumerate all subkeys (immediately) under the given node. nsCOMPtr enumKeys; rv = oldReg->EnumerateSubtrees(nsIRegistry::Users, getter_AddRefs(enumKeys)); if (NS_FAILED(rv)) return rv; rv = enumKeys->First(); if (NS_FAILED(rv)) return rv; if (fromImport) m4xProfilesAdded = PR_TRUE; // Enumerate subkeys till done. while( (NS_OK != enumKeys->IsDone())) { nsCOMPtr base; rv = enumKeys->CurrentItem(getter_AddRefs(base)); if (NS_FAILED(rv)) return rv; // Get specific interface. nsCOMPtr node; nsIID nodeIID = NS_IREGISTRYNODE_IID; rv = base->QueryInterface( nodeIID, getter_AddRefs(node)); if (NS_FAILED(rv)) return rv; nsXPIDLString profile; rv = node->GetName(getter_Copies(profile)); if (NS_FAILED(rv)) return rv; // Unescape is done on the profileName to interpret special characters like %, _ etc. // For example something like %20 would probably be interpreted as a space // There is some problem I guess in sending a space as itself // NOTE: This needs to be done BEFORE the test for existence. #if defined(XP_MAC) || defined(XP_MACOSX) // 4.x profiles coming from japanese machine are already in unicode. // So, there is no need to decode into unicode further. NS_ConvertUTF16toUTF8 temp(profile); nsCAutoString profileName(nsUnescape(temp.BeginWriting())); NS_ConvertUTF8toUTF16 convertedProfName(profileName); #else nsCAutoString temp; NS_CopyUnicodeToNative(profile, temp); nsCAutoString profileName(nsUnescape(temp.BeginWriting())); nsAutoString convertedProfName; NS_CopyNativeToUnicode(profileName, convertedProfName); #endif PRBool exists = PR_FALSE; if (!fromImport) { exists = ProfileExists(convertedProfName.get()); if (exists) { rv = enumKeys->Next(); if (NS_FAILED(rv)) return rv; continue; } } nsRegistryKey key; rv = node->GetKey(&key); if (NS_FAILED(rv)) return rv; ProfileStruct* profileItem = new ProfileStruct(); if (!profileItem) return NS_ERROR_OUT_OF_MEMORY; profileItem->updateProfileEntry = PR_TRUE; profileItem->profileName = convertedProfName; rv = profileItem->InternalizeLocation(oldReg, key, PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(rv), "Could not get 4x profile location"); profileItem->isMigrated = PR_FALSE; profileItem->isImportType = fromImport; SetValue(profileItem); delete profileItem; rv = enumKeys->Next(); if (NS_FAILED(rv)) return rv; } #elif defined (XP_BEOS) #else /* XP_UNIX */ nsCAutoString unixProfileName( PR_GetEnv(PROFILE_NAME_ENVIRONMENT_VARIABLE) ); nsCAutoString unixProfileDirectory( PR_GetEnv(PROFILE_HOME_ENVIRONMENT_VARIABLE) ); if (unixProfileName.IsEmpty() || unixProfileDirectory.IsEmpty()) { unixProfileDirectory = PR_GetEnv(HOME_ENVIRONMENT_VARIABLE); unixProfileName = PR_GetEnv(LOGNAME_ENVIRONMENT_VARIABLE); if ( unixProfileName.IsEmpty() ) { unixProfileName = PR_GetEnv(USER_ENVIRONMENT_VARIABLE); } if ( unixProfileName.IsEmpty() ) { unixProfileName = DEFAULT_UNIX_PROFILE_NAME; } } PRBool exists = PR_FALSE; if (!fromImport) { nsAutoString profileNameUTF16; NS_CopyNativeToUnicode(unixProfileName, profileNameUTF16); exists = ProfileExists(profileNameUTF16.get()); if (exists) { return NS_OK; } } if ( ! unixProfileName.IsEmpty() && ! unixProfileDirectory.IsEmpty() ) { nsCAutoString profileLocation(unixProfileDirectory); profileLocation += "/.netscape"; nsCOMPtr fileInNSDir; rv = NS_NewNativeLocalFile(profileLocation + NS_LITERAL_CSTRING("/preferences.js"), PR_TRUE, getter_AddRefs(fileInNSDir)); if (NS_FAILED(rv)) return rv; rv = fileInNSDir->Exists(&exists); #ifdef DEBUG printf("%s/preferences.js exists: %d\n",profileLocation.get(), NS_SUCCEEDED(rv) && exists); #endif if (NS_SUCCEEDED(rv) && exists) { ProfileStruct* profileItem = new ProfileStruct(); if (!profileItem) return NS_ERROR_OUT_OF_MEMORY; profileItem->updateProfileEntry = PR_TRUE; NS_CopyNativeToUnicode(unixProfileName, profileItem->profileName); nsCOMPtr localFile; rv = NS_NewNativeLocalFile(profileLocation, PR_TRUE, getter_AddRefs(localFile)); if (NS_FAILED(rv)) return rv; profileItem->SetResolvedProfileDir(localFile); profileItem->isMigrated = PR_FALSE; profileItem->isImportType = fromImport; SetValue(profileItem); delete profileItem; } else { #ifdef DEBUG printf("no 4.x profile\n"); #endif } } #endif /* XP_UNIX */ return rv; } // Set the PREG flag to indicate if that info exists void nsProfileAccess::SetPREGInfo(const char* pregInfo) { NS_ASSERTION(pregInfo, "Invalid pregInfo"); // This is always going to be just a yes/no string mHavePREGInfo.AssignWithConversion(pregInfo); } //Get the for PREG info. void nsProfileAccess::CheckRegString(const PRUnichar *profileName, char **info) { NS_ASSERTION(profileName, "Invalid profile name"); NS_ASSERTION(info, "Invalid info pointer"); *info = nsnull; PRInt32 index = 0; index = FindProfileIndex(profileName, PR_FALSE); if (index >= 0 ) { ProfileStruct* profileItem = (ProfileStruct *) (mProfiles->ElementAt(index)); if (!profileItem->NCHavePregInfo.IsEmpty()) { *info = ToNewCString(profileItem->NCHavePregInfo); } else { *info = ToNewCString(kRegistryNoString); } } } // Clear the profile member data structure // We need to fill in the data from the new registry nsresult nsProfileAccess::ResetProfileMembers() { FreeProfileMembers(mProfiles); mProfiles = new nsVoidArray(); return NS_OK; } nsresult nsProfileAccess::DetermineForceMigration(PRBool *forceMigration) { if (!forceMigration) return NS_ERROR_NULL_POINTER; PRInt32 numProfiles; GetNumProfiles(&numProfiles); if (numProfiles > 0) { // we have some 6.0 profiles, don't force migration: *forceMigration = PR_FALSE; return NS_OK; } // even if we don't any 4.x profiles, running -installer is safe. so do it *forceMigration = PR_TRUE; return NS_OK; } // ********************************************************************** // class ProfileStruct // ********************************************************************** ProfileStruct::ProfileStruct() : isMigrated(PR_FALSE), updateProfileEntry(PR_FALSE), isImportType(PR_FALSE), creationTime(LL_ZERO), lastModTime(LL_ZERO) { } ProfileStruct::ProfileStruct(const ProfileStruct& src) { *this = src; } ProfileStruct& ProfileStruct::operator=(const ProfileStruct& rhs) { profileName = rhs.profileName; isMigrated = rhs.isMigrated; NCProfileName = rhs.NCProfileName; NCDeniedService = rhs.NCDeniedService; NCEmailAddress = rhs.NCEmailAddress; NCHavePregInfo = rhs.NCHavePregInfo; updateProfileEntry = rhs.updateProfileEntry; isImportType = rhs.isImportType; creationTime = rhs.creationTime; lastModTime = rhs.lastModTime; nsresult rv; nsCOMPtr file; resolvedLocation = nsnull; if (rhs.resolvedLocation) { regLocationData.Truncate(0); rv = rhs.resolvedLocation->Clone(getter_AddRefs(file)); if (NS_SUCCEEDED(rv)) resolvedLocation = do_QueryInterface(file); file = nsnull; } else regLocationData = rhs.regLocationData; migratedFrom = nsnull; if (rhs.migratedFrom) { rv = rhs.migratedFrom->Clone(getter_AddRefs(file)); if (NS_SUCCEEDED(rv)) migratedFrom = do_QueryInterface(file); } return *this; } nsresult ProfileStruct::GetResolvedProfileDir(nsILocalFile **aDirectory) { NS_ENSURE_ARG_POINTER(aDirectory); *aDirectory = nsnull; if (resolvedLocation) { *aDirectory = resolvedLocation; NS_ADDREF(*aDirectory); return NS_OK; } return NS_ERROR_FILE_NOT_FOUND; // The only reason it would not exist. } nsresult ProfileStruct::SetResolvedProfileDir(nsILocalFile *aDirectory) { NS_ENSURE_ARG(aDirectory); resolvedLocation = aDirectory; regLocationData.SetLength(0); return NS_OK; } nsresult ProfileStruct::CopyProfileLocation(ProfileStruct *destStruct) { if (resolvedLocation) { nsCOMPtr file; nsresult rv = resolvedLocation->Clone(getter_AddRefs(file)); if (NS_SUCCEEDED(rv)) destStruct->resolvedLocation = do_QueryInterface(file, &rv); if (NS_FAILED(rv)) return rv; } destStruct->regLocationData = regLocationData; return NS_OK; } nsresult ProfileStruct::InternalizeLocation(nsIRegistry *aRegistry, nsRegistryKey profKey, PRBool is4x) { nsresult rv; nsCOMPtr tempLocal; // Reset ourselves regLocationData.SetLength(0); resolvedLocation = nsnull; if (is4x) { nsXPIDLString profLoc; rv = aRegistry->GetString( profKey, NS_LITERAL_STRING("ProfileLocation").get(), getter_Copies(profLoc)); if (NS_FAILED(rv)) return rv; regLocationData = profLoc; #if defined(XP_MAC) || defined(XP_MACOSX) // 4.x profiles coming from japanese machine are already in unicode. // So, there is no need to decode into unicode further. // Unescape profile location NS_ConvertUTF16toUTF8 tempLoc(profLoc); nsCAutoString profileLocation(nsUnescape(tempLoc.BeginWriting())); NS_ConvertUTF8toUTF16 convertedProfLoc(profileLocation); // Now we have a unicode path - make it into a file // This is an HFS style path, which can't be used with nsIFile, so convert it. rv = NS_ERROR_FAILURE; CFStringRef pathStrRef = ::CFStringCreateWithCharacters(NULL, convertedProfLoc.get(), convertedProfLoc.Length()); if (pathStrRef) { CFURLRef pathURLRef = ::CFURLCreateWithFileSystemPath(NULL, pathStrRef, kCFURLHFSPathStyle, true); if (pathURLRef) { rv = NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(tempLocal)); if (NS_SUCCEEDED(rv)) { nsCOMPtr tempLocalMac(do_QueryInterface(tempLocal)); rv = tempLocalMac->InitWithCFURL(pathURLRef); } ::CFRelease(pathURLRef); } ::CFRelease(pathStrRef); } #else // Unescape profile location and convert it to the right format nsCAutoString tempLoc; rv = NS_CopyUnicodeToNative(profLoc, tempLoc); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to convert profile location to native encoding"); nsCAutoString profileLocation(nsUnescape(tempLoc.BeginWriting())); rv = NS_NewNativeLocalFile(profileLocation, PR_TRUE, getter_AddRefs(tempLocal)); #endif } else { nsXPIDLString regData; rv = aRegistry->GetString(profKey, kRegistryDirectoryString.get(), getter_Copies(regData)); if (NS_FAILED(rv)) return rv; regLocationData = regData; #if defined(XP_MAC) || defined(XP_MACOSX) rv = NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(tempLocal)); if (NS_SUCCEEDED(rv)) // regLocationData is ASCII so no loss rv = tempLocal->SetPersistentDescriptor(NS_LossyConvertUTF16toASCII(regLocationData)); #else rv = NS_NewLocalFile(regLocationData, PR_TRUE, getter_AddRefs(tempLocal)); #endif } if (NS_SUCCEEDED(rv) && tempLocal) { PRBool exists; if (NS_SUCCEEDED(tempLocal->Exists(&exists)) && exists) SetResolvedProfileDir(tempLocal); } return NS_OK; } nsresult ProfileStruct::ExternalizeLocation(nsIRegistry *aRegistry, nsRegistryKey profKey) { nsresult rv; if (resolvedLocation) { nsAutoString regData; #if defined(XP_MAC) || defined(XP_MACOSX) PRBool leafCreated; nsCAutoString descBuf; // It must exist before we try to use GetPersistentDescriptor rv = EnsureDirPathExists(resolvedLocation, &leafCreated); if (NS_FAILED(rv)) return rv; rv = resolvedLocation->GetPersistentDescriptor(descBuf); if (NS_FAILED(rv)) return rv; if (leafCreated) resolvedLocation->Remove(PR_FALSE); AppendUTF8toUTF16(descBuf, regData); #else rv = resolvedLocation->GetPath(regData); if (NS_FAILED(rv)) return rv; #endif rv = aRegistry->SetString(profKey, kRegistryDirectoryString.get(), regData.get()); } else if (!regLocationData.IsEmpty()) { // Write the original data back out - maybe it can be resolved later. rv = aRegistry->SetString(profKey, kRegistryDirectoryString.get(), regLocationData.get()); } else { NS_ASSERTION(PR_FALSE, "ProfileStruct has no location data!"); rv = NS_ERROR_FAILURE; } return rv; } nsresult ProfileStruct::InternalizeMigratedFromLocation(nsIRegistry *aRegistry, nsRegistryKey profKey) { nsresult rv; nsXPIDLCString regData; nsCOMPtr tempLocal; rv = aRegistry->GetStringUTF8(profKey, kRegistryMigratedFromString.get(), getter_Copies(regData)); if (NS_SUCCEEDED(rv)) { #if defined(XP_MAC) || defined(XP_MACOSX) rv = NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(tempLocal)); if (NS_SUCCEEDED(rv)) { // The persistent desc on Mac is base64 encoded so plain ASCII rv = tempLocal->SetPersistentDescriptor(regData); if (NS_SUCCEEDED(rv)) migratedFrom = tempLocal; } #else rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(regData), PR_TRUE, getter_AddRefs(tempLocal)); if (NS_SUCCEEDED(rv)) migratedFrom = tempLocal; #endif } return rv; } nsresult ProfileStruct::ExternalizeMigratedFromLocation(nsIRegistry *aRegistry, nsRegistryKey profKey) { nsresult rv = NS_OK; nsCAutoString regData; if (migratedFrom) { #if defined(XP_MAC) || defined(XP_MACOSX) rv = migratedFrom->GetPersistentDescriptor(regData); #else nsAutoString path; rv = migratedFrom->GetPath(path); AppendUTF16toUTF8(path, regData); #endif if (NS_SUCCEEDED(rv)) rv = aRegistry->SetStringUTF8(profKey, kRegistryMigratedFromString.get(), regData.get()); } return rv; } nsresult ProfileStruct::EnsureDirPathExists(nsILocalFile *aDir, PRBool *wasCreated) { NS_ENSURE_ARG(aDir); NS_ENSURE_ARG_POINTER(wasCreated); *wasCreated = PR_FALSE; nsresult rv; PRBool exists; rv = aDir->Exists(&exists); if (NS_SUCCEEDED(rv) && !exists) { rv = aDir->Create(nsIFile::DIRECTORY_TYPE, 0700); *wasCreated = NS_SUCCEEDED(rv); } return rv; }