michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsProfileDirServiceProvider.h" michael@0: #include "nsProfileStringTypes.h" michael@0: #include "nsProfileLock.h" michael@0: #include "nsIFile.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsISupportsUtils.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: #include "nsIObserverService.h" michael@0: michael@0: // File Name Defines michael@0: michael@0: #define PREFS_FILE_50_NAME NS_LITERAL_CSTRING("prefs.js") michael@0: #define PREFS_FILE_METRO_50_NAME NS_LITERAL_CSTRING("metro-prefs.js") michael@0: #define USER_CHROME_DIR_50_NAME NS_LITERAL_CSTRING("chrome") michael@0: #define LOCAL_STORE_FILE_50_NAME NS_LITERAL_CSTRING("localstore.rdf") michael@0: #define PANELS_FILE_50_NAME NS_LITERAL_CSTRING("panels.rdf") michael@0: #define MIME_TYPES_FILE_50_NAME NS_LITERAL_CSTRING("mimeTypes.rdf") michael@0: #define BOOKMARKS_FILE_50_NAME NS_LITERAL_CSTRING("bookmarks.html") michael@0: #define DOWNLOADS_FILE_50_NAME NS_LITERAL_CSTRING("downloads.rdf") michael@0: #define SEARCH_FILE_50_NAME NS_LITERAL_CSTRING("search.rdf" ) michael@0: michael@0: //***************************************************************************** michael@0: // nsProfileDirServiceProvider::nsProfileDirServiceProvider michael@0: //***************************************************************************** michael@0: michael@0: nsProfileDirServiceProvider::nsProfileDirServiceProvider(bool aNotifyObservers) : michael@0: #ifdef MOZ_PROFILELOCKING michael@0: mProfileDirLock(nullptr), michael@0: #endif michael@0: mNotifyObservers(aNotifyObservers), michael@0: mSharingEnabled(false) michael@0: { michael@0: } michael@0: michael@0: michael@0: nsProfileDirServiceProvider::~nsProfileDirServiceProvider() michael@0: { michael@0: #ifdef MOZ_PROFILELOCKING michael@0: delete mProfileDirLock; michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: nsProfileDirServiceProvider::SetProfileDir(nsIFile* aProfileDir, michael@0: nsIFile* aLocalProfileDir) michael@0: { michael@0: if (!aLocalProfileDir) michael@0: aLocalProfileDir = aProfileDir; michael@0: if (mProfileDir) { michael@0: bool isEqual; michael@0: if (aProfileDir && michael@0: NS_SUCCEEDED(aProfileDir->Equals(mProfileDir, &isEqual)) && isEqual) { michael@0: NS_WARNING("Setting profile dir to same as current"); michael@0: return NS_OK; michael@0: } michael@0: #ifdef MOZ_PROFILELOCKING michael@0: mProfileDirLock->Unlock(); michael@0: #endif michael@0: UndefineFileLocations(); michael@0: } michael@0: mProfileDir = aProfileDir; michael@0: mLocalProfileDir = aLocalProfileDir; michael@0: if (!mProfileDir) michael@0: return NS_OK; michael@0: michael@0: nsresult rv = InitProfileDir(mProfileDir); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // Make sure that the local profile dir exists michael@0: // we just try to create it - if it exists already, that'll fail; ignore michael@0: // errors michael@0: mLocalProfileDir->Create(nsIFile::DIRECTORY_TYPE, 0700); michael@0: michael@0: #ifdef MOZ_PROFILELOCKING michael@0: // Lock the non-shared sub-dir if we are sharing, michael@0: // the whole profile dir if we are not. michael@0: nsCOMPtr dirToLock; michael@0: if (mSharingEnabled) michael@0: dirToLock = mNonSharedProfileDir; michael@0: else michael@0: dirToLock = mProfileDir; michael@0: rv = mProfileDirLock->Lock(dirToLock, nullptr); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: #endif michael@0: michael@0: if (mNotifyObservers) { michael@0: nsCOMPtr observerService = michael@0: do_GetService("@mozilla.org/observer-service;1"); michael@0: if (!observerService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NS_NAMED_LITERAL_STRING(context, "startup"); michael@0: // Notify observers that the profile has changed - Here they respond to new profile michael@0: observerService->NotifyObservers(nullptr, "profile-do-change", context.get()); michael@0: // Now observers can respond to something another observer did on "profile-do-change" michael@0: observerService->NotifyObservers(nullptr, "profile-after-change", context.get()); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsProfileDirServiceProvider::Register() michael@0: { michael@0: nsCOMPtr directoryService = michael@0: do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); michael@0: if (!directoryService) michael@0: return NS_ERROR_FAILURE; michael@0: return directoryService->RegisterProvider(this); michael@0: } michael@0: michael@0: nsresult michael@0: nsProfileDirServiceProvider::Shutdown() michael@0: { michael@0: if (!mNotifyObservers) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr observerService = michael@0: do_GetService("@mozilla.org/observer-service;1"); michael@0: if (!observerService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NS_NAMED_LITERAL_STRING(context, "shutdown-persist"); michael@0: observerService->NotifyObservers(nullptr, "profile-before-change", context.get()); michael@0: observerService->NotifyObservers(nullptr, "profile-before-change2", context.get()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsProfileDirServiceProvider::nsISupports michael@0: //***************************************************************************** michael@0: michael@0: NS_IMPL_ISUPPORTS(nsProfileDirServiceProvider, michael@0: nsIDirectoryServiceProvider) michael@0: michael@0: //***************************************************************************** michael@0: // nsProfileDirServiceProvider::nsIDirectoryServiceProvider michael@0: //***************************************************************************** michael@0: michael@0: NS_IMETHODIMP michael@0: nsProfileDirServiceProvider::GetFile(const char *prop, bool *persistant, nsIFile **_retval) michael@0: { michael@0: NS_ENSURE_ARG(prop); michael@0: NS_ENSURE_ARG_POINTER(persistant); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: // Don't assert - we can be called many times before SetProfileDir() has been called. michael@0: if (!mProfileDir) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *persistant = true; michael@0: nsIFile* domainDir = mProfileDir; michael@0: michael@0: nsCOMPtr localFile; michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: if (strcmp(prop, NS_APP_PREFS_50_DIR) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: } michael@0: else if (strcmp(prop, NS_APP_PREFS_50_FILE) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = localFile->AppendNative(PREFS_FILE_50_NAME); michael@0: } michael@0: else if (strcmp(prop, NS_METRO_APP_PREFS_50_FILE) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = localFile->AppendNative(PREFS_FILE_METRO_50_NAME); michael@0: } michael@0: else if (strcmp(prop, NS_APP_USER_PROFILE_50_DIR) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: } michael@0: else if (strcmp(prop, NS_APP_USER_PROFILE_LOCAL_50_DIR) == 0) { michael@0: rv = mLocalProfileDir->Clone(getter_AddRefs(localFile)); michael@0: } michael@0: else if (strcmp(prop, NS_APP_USER_CHROME_DIR) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = localFile->AppendNative(USER_CHROME_DIR_50_NAME); michael@0: } michael@0: else if (strcmp(prop, NS_APP_LOCALSTORE_50_FILE) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = localFile->AppendNative(LOCAL_STORE_FILE_50_NAME); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // it's OK if we can't copy the file... it will be created michael@0: // by client code. michael@0: (void) EnsureProfileFileExists(localFile, domainDir); michael@0: } michael@0: } michael@0: } michael@0: else if (strcmp(prop, NS_APP_USER_PANELS_50_FILE) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = localFile->AppendNative(PANELS_FILE_50_NAME); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = EnsureProfileFileExists(localFile, domainDir); michael@0: } michael@0: } michael@0: else if (strcmp(prop, NS_APP_USER_MIMETYPES_50_FILE) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = localFile->AppendNative(MIME_TYPES_FILE_50_NAME); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = EnsureProfileFileExists(localFile, domainDir); michael@0: } michael@0: } michael@0: else if (strcmp(prop, NS_APP_BOOKMARKS_50_FILE) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = localFile->AppendNative(BOOKMARKS_FILE_50_NAME); michael@0: } michael@0: else if (strcmp(prop, NS_APP_DOWNLOADS_50_FILE) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = localFile->AppendNative(DOWNLOADS_FILE_50_NAME); michael@0: } michael@0: else if (strcmp(prop, NS_APP_SEARCH_50_FILE) == 0) { michael@0: rv = domainDir->Clone(getter_AddRefs(localFile)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = localFile->AppendNative(SEARCH_FILE_50_NAME); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = EnsureProfileFileExists(localFile, domainDir); michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: localFile.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // Protected methods michael@0: //***************************************************************************** michael@0: michael@0: nsresult michael@0: nsProfileDirServiceProvider::Initialize() michael@0: { michael@0: #ifdef MOZ_PROFILELOCKING michael@0: mProfileDirLock = new nsProfileLock; michael@0: if (!mProfileDirLock) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsProfileDirServiceProvider::InitProfileDir(nsIFile *profileDir) michael@0: { michael@0: // Make sure our "Profile" folder exists. michael@0: // If it does not, copy the profile defaults to its location. michael@0: michael@0: nsresult rv; michael@0: bool exists; michael@0: rv = profileDir->Exists(&exists); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (!exists) { michael@0: nsCOMPtr profileDefaultsDir; michael@0: nsCOMPtr profileDirParent; michael@0: nsAutoCString profileDirName; michael@0: michael@0: (void)profileDir->GetParent(getter_AddRefs(profileDirParent)); michael@0: if (!profileDirParent) michael@0: return NS_ERROR_FAILURE; michael@0: rv = profileDir->GetNativeLeafName(profileDirName); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_50_DIR, getter_AddRefs(profileDefaultsDir)); michael@0: if (NS_FAILED(rv)) { michael@0: rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR, getter_AddRefs(profileDefaultsDir)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: rv = profileDefaultsDir->CopyToNative(profileDirParent, profileDirName); michael@0: if (NS_FAILED(rv)) { michael@0: // if copying failed, lets just ensure that the profile directory exists. michael@0: profileDirParent->AppendNative(profileDirName); michael@0: rv = profileDirParent->Create(nsIFile::DIRECTORY_TYPE, 0700); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: rv = profileDir->SetPermissions(0700); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: else { michael@0: bool isDir; michael@0: rv = profileDir->IsDirectory(&isDir); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (!isDir) michael@0: return NS_ERROR_FILE_NOT_DIRECTORY; michael@0: } michael@0: michael@0: if (mNonSharedDirName.Length()) michael@0: rv = InitNonSharedProfileDir(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsProfileDirServiceProvider::InitNonSharedProfileDir() michael@0: { michael@0: nsresult rv; michael@0: michael@0: NS_ENSURE_STATE(mProfileDir); michael@0: NS_ENSURE_STATE(mNonSharedDirName.Length()); michael@0: michael@0: nsCOMPtr localDir; michael@0: rv = mProfileDir->Clone(getter_AddRefs(localDir)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = localDir->Append(mNonSharedDirName); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: bool exists; michael@0: rv = localDir->Exists(&exists); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (!exists) { michael@0: rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700); michael@0: } michael@0: else { michael@0: bool isDir; michael@0: rv = localDir->IsDirectory(&isDir); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (!isDir) michael@0: rv = NS_ERROR_FILE_NOT_DIRECTORY; michael@0: } michael@0: } michael@0: if (NS_SUCCEEDED(rv)) michael@0: mNonSharedProfileDir = localDir; michael@0: } michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsProfileDirServiceProvider::EnsureProfileFileExists(nsIFile *aFile, nsIFile *destDir) michael@0: { michael@0: nsresult rv; michael@0: bool exists; michael@0: michael@0: rv = aFile->Exists(&exists); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (exists) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr defaultsFile; michael@0: michael@0: // Attempt first to get the localized subdir of the defaults michael@0: rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_50_DIR, getter_AddRefs(defaultsFile)); michael@0: if (NS_FAILED(rv)) { michael@0: // If that has not been defined, use the top level of the defaults michael@0: rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR, getter_AddRefs(defaultsFile)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: nsAutoCString leafName; michael@0: rv = aFile->GetNativeLeafName(leafName); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = defaultsFile->AppendNative(leafName); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return defaultsFile->CopyTo(destDir, EmptyString()); michael@0: } michael@0: michael@0: nsresult michael@0: nsProfileDirServiceProvider::UndefineFileLocations() michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr directoryService = michael@0: do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_TRUE(directoryService, NS_ERROR_FAILURE); michael@0: michael@0: (void) directoryService->Undefine(NS_APP_PREFS_50_DIR); michael@0: (void) directoryService->Undefine(NS_APP_PREFS_50_FILE); michael@0: (void) directoryService->Undefine(NS_APP_USER_PROFILE_50_DIR); michael@0: (void) directoryService->Undefine(NS_APP_USER_CHROME_DIR); michael@0: (void) directoryService->Undefine(NS_APP_LOCALSTORE_50_FILE); michael@0: (void) directoryService->Undefine(NS_APP_USER_PANELS_50_FILE); michael@0: (void) directoryService->Undefine(NS_APP_USER_MIMETYPES_50_FILE); michael@0: (void) directoryService->Undefine(NS_APP_BOOKMARKS_50_FILE); michael@0: (void) directoryService->Undefine(NS_APP_DOWNLOADS_50_FILE); michael@0: (void) directoryService->Undefine(NS_APP_SEARCH_50_FILE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // Global creation function michael@0: //***************************************************************************** michael@0: michael@0: nsresult NS_NewProfileDirServiceProvider(bool aNotifyObservers, michael@0: nsProfileDirServiceProvider** aProvider) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aProvider); michael@0: *aProvider = nullptr; michael@0: michael@0: nsProfileDirServiceProvider *prov = new nsProfileDirServiceProvider(aNotifyObservers); michael@0: if (!prov) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: nsresult rv = prov->Initialize(); michael@0: if (NS_FAILED(rv)) { michael@0: delete prov; michael@0: return rv; michael@0: } michael@0: NS_ADDREF(*aProvider = prov); michael@0: return NS_OK; michael@0: }