// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "build/build_config.h" #include "chrome/browser/user_data_manager.h" #include #include "base/file_util.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/string_util.h" #include "chrome/browser/chrome_thread.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/l10n_util.h" #include "chrome/installer/util/browser_distribution.h" #include "grit/chromium_strings.h" #if defined(OS_WIN) #include #include "chrome/installer/util/shell_util.h" #endif namespace { // Helper to start chrome for a given profile index. The helper takes care of // enumerating profiles on the file thread and then it launches Chrome for the // appropriate profile on the original thread. // An instance of this class should always be created on the heap, and it will // delete itself when the launch is done. class LaunchChromeForProfileIndexHelper : GetProfilesHelper::Delegate { public: // Creates an instance with the given data manager and to launch chrome for // the profile with the given index. LaunchChromeForProfileIndexHelper(const UserDataManager* manager, int index); virtual ~LaunchChromeForProfileIndexHelper(); // Starts the asynchronous launch. void StartLaunch(); // GetProfilesHelper::Delegate method. void OnGetProfilesDone(const std::vector& profiles); private: int index_; const UserDataManager* manager_; scoped_refptr profiles_helper_; DISALLOW_COPY_AND_ASSIGN(LaunchChromeForProfileIndexHelper); }; } // namespace LaunchChromeForProfileIndexHelper::LaunchChromeForProfileIndexHelper( const UserDataManager* manager, int index) : index_(index), manager_(manager), ALLOW_THIS_IN_INITIALIZER_LIST( profiles_helper_(new GetProfilesHelper(this))) { DCHECK(manager); } LaunchChromeForProfileIndexHelper::~LaunchChromeForProfileIndexHelper() { profiles_helper_->OnDelegateDeleted(); } void LaunchChromeForProfileIndexHelper::StartLaunch() { profiles_helper_->GetProfiles(NULL); } void LaunchChromeForProfileIndexHelper::OnGetProfilesDone( const std::vector& profiles) { if (index_ >= 0 && index_ < static_cast(profiles.size())) manager_->LaunchChromeForProfile(profiles[index_]); // We are done, delete ourselves. delete this; } // Separator used in folder names between the prefix and the profile name. // For e.g. a folder for the profile "Joe" would be named "User Data-Joe". static const wchar_t kProfileFolderSeparator[] = L"-"; // static UserDataManager* UserDataManager::instance_ = NULL; // static UserDataManager* UserDataManager::Create() { DCHECK(!instance_); std::wstring user_data; PathService::Get(chrome::DIR_USER_DATA, &user_data); instance_ = new UserDataManager(user_data); return instance_; } // static UserDataManager* UserDataManager::Get() { DCHECK(instance_); return instance_; } UserDataManager::UserDataManager(const std::wstring& user_data_root) : user_data_root_(user_data_root) { // Determine current profile name and current folder name. current_folder_name_ = file_util::GetFilenameFromPath(user_data_root); bool success = GetProfileNameFromFolderName(current_folder_name_, ¤t_profile_name_); // The current profile is a default profile if the current user data folder // name is just kUserDataDirname or when the folder name doesn't have the // kUserDataDirname as prefix. is_current_profile_default_ = !success || (current_folder_name_ == chrome::kUserDataDirname); // (TODO:munjal) Fix issue 5070: // http://code.google.com/p/chromium/issues/detail?id=5070 file_util::UpOneDirectory(&user_data_root_); } UserDataManager::~UserDataManager() { if (instance_ == this) instance_ = NULL; } // static bool UserDataManager::GetProfileNameFromFolderName( const std::wstring& folder_name, std::wstring* profile_name) { // The folder name should start with a specific prefix for it to be a valid // profile folder. if (folder_name.find(chrome::kUserDataDirname) != 0) return false; // Seems like we cannot use arraysize macro for externally defined constants. // Is there a way? unsigned int prefix_length = wcslen(chrome::kUserDataDirname); // Subtract 1 from the size of the array for trailing null character. unsigned int separator_length = arraysize(kProfileFolderSeparator) - 1; // It's safe to use profile_name variable for intermediate values since we // will always return true now. *profile_name = folder_name.substr(prefix_length); // Remove leading separator if present. if (profile_name->find_first_of(kProfileFolderSeparator) == 0) *profile_name = profile_name->substr(separator_length); if (profile_name->empty()) *profile_name = chrome::kNotSignedInProfile; return true; } // static std::wstring UserDataManager::GetFolderNameFromProfileName( const std::wstring& profile_name) { std::wstring folder_name = chrome::kUserDataDirname; if (profile_name != chrome::kNotSignedInProfile) { folder_name += kProfileFolderSeparator; folder_name += profile_name; } return folder_name; } std::wstring UserDataManager::GetUserDataFolderForProfile( const std::wstring& profile_name) const { std::wstring folder_name = GetFolderNameFromProfileName(profile_name); std::wstring folder_path(user_data_root_); file_util::AppendToPath(&folder_path, folder_name); return folder_path; } void UserDataManager::LaunchChromeForProfile( const std::wstring& profile_name) const { std::wstring user_data_dir = GetUserDataFolderForProfile(profile_name); std::wstring command; PathService::Get(base::FILE_EXE, &command); CommandLine command_line(command); command_line.AppendSwitch(switches::kEnableUserDataDirProfiles); command_line.AppendSwitchWithValue(switches::kUserDataDir, user_data_dir); std::wstring local_state_path; PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path); command_line.AppendSwitchWithValue(switches::kParentProfile, local_state_path); base::LaunchApp(command_line, false, false, NULL); } void UserDataManager::LaunchChromeForProfile(int index) const { // Helper deletes itself when done. LaunchChromeForProfileIndexHelper* helper = new LaunchChromeForProfileIndexHelper(this, index); helper->StartLaunch(); } void UserDataManager::GetProfiles(std::vector* profiles) const { // This function should be called on the file thread. DCHECK(MessageLoop::current() == ChromeThread::GetMessageLoop(ChromeThread::FILE)); file_util::FileEnumerator file_enum( FilePath::FromWStringHack(user_data_root_), false, file_util::FileEnumerator::DIRECTORIES); std::wstring folder_name; while (!(folder_name = file_enum.Next().ToWStringHack()).empty()) { folder_name = file_util::GetFilenameFromPath(folder_name); std::wstring profile_name; if (GetProfileNameFromFolderName(folder_name, &profile_name)) profiles->push_back(profile_name); } } bool UserDataManager::CreateDesktopShortcutForProfile( const std::wstring& profile_name) const { #if defined(OS_WIN) std::wstring exe_path; std::wstring shortcut_path; if (!PathService::Get(base::FILE_EXE, &exe_path) || !ShellUtil::GetDesktopPath(false, &shortcut_path)) return false; // Working directory. std::wstring exe_folder = file_util::GetDirectoryFromPath(exe_path); // Command and arguments. std::wstring cmd; cmd = StringPrintf(L"\"%ls\"", exe_path.c_str()); std::wstring user_data_dir = GetUserDataFolderForProfile(profile_name); std::wstring args = CommandLine::PrefixedSwitchStringWithValue( switches::kUserDataDir, user_data_dir); args = StringPrintf(L"\"%ls\"", args.c_str()); // Shortcut path. std::wstring shortcut_name = l10n_util::GetStringF( IDS_START_IN_PROFILE_SHORTCUT_NAME, profile_name); shortcut_name.append(L".lnk"); file_util::AppendToPath(&shortcut_path, shortcut_name); return file_util::CreateShortcutLink(cmd.c_str(), shortcut_path.c_str(), exe_folder.c_str(), args.c_str(), NULL, exe_path.c_str(), 0); #else // TODO(port): should probably use freedesktop.org standard for desktop files. NOTIMPLEMENTED(); return false; #endif } GetProfilesHelper::GetProfilesHelper(Delegate* delegate) : delegate_(delegate) { } void GetProfilesHelper::GetProfiles(MessageLoop* target_loop) { // If the target loop is not NULL then use the target loop, or if it's NULL // then use the current message loop to post a task on it later when we are // done building a list of profiles. if (target_loop) { message_loop_ = target_loop; } else { message_loop_ = MessageLoop::current(); } DCHECK(message_loop_); MessageLoop* file_loop = ChromeThread::GetMessageLoop(ChromeThread::FILE); file_loop->PostTask( FROM_HERE, NewRunnableMethod(this, &GetProfilesHelper::GetProfilesFromManager)); } // Records that the delegate is closed. void GetProfilesHelper::OnDelegateDeleted() { delegate_ = NULL; } void GetProfilesHelper::GetProfilesFromManager() { // This function should be called on the file thread. DCHECK(MessageLoop::current() == ChromeThread::GetMessageLoop(ChromeThread::FILE)); // If the delegate is gone by now, no need to do any work. if (!delegate_) return; scoped_ptr< std::vector > profiles( new std::vector); UserDataManager::Get()->GetProfiles(profiles.get()); // Post a task on the original thread to call the delegate. message_loop_->PostTask( FROM_HERE, NewRunnableMethod(this, &GetProfilesHelper::InvokeDelegate, profiles.release())); } void GetProfilesHelper::InvokeDelegate(std::vector* profiles) { scoped_ptr< std::vector > udd_profiles(profiles); // If the delegate is gone by now, no need to do any work. if (delegate_) delegate_->OnGetProfilesDone(*udd_profiles.get()); }