diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/installer/util | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer/util')
49 files changed, 6433 insertions, 0 deletions
diff --git a/chrome/installer/util/SConscript b/chrome/installer/util/SConscript new file mode 100644 index 0000000..7a517cf --- /dev/null +++ b/chrome/installer/util/SConscript @@ -0,0 +1,106 @@ +# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env')
+
+env = env.Clone()
+
+
+env.Prepend(
+ CPPPATH = [
+ Dir("#/../third_party/lzma_sdk"),
+ Dir("../chrome/third_party/wtl/include"),
+ Dir("#/../third_party/npapi"),
+ Dir("#/../third_party/libxml/include"),
+ #/I "C:/src/trunk/chrome/Debug/obj/generated_resources"
+ #/I "C:/src/trunk/chrome/Debug/obj/localized_strings"
+ Dir("#/../skia/include"),
+ Dir("#/../skia/include/corecg"),
+ Dir("#/../skia/platform"),
+ Dir("#/../third_party/libpng"),
+ Dir("#/../third_party/zlib"),
+ Dir("#/../breakpad/src"),
+ Dir("#/../third_party/libjpeg"),
+ Dir("#/../third_party/icu38/public/common"),
+ Dir("#/../third_party/icu38/public/i18n"),
+ Dir("#/.."),
+ Dir("."),
+ ],
+ CPPDEFINES = [
+ "_LZMA_IN_CB",
+ "LIBXML_STATIC",
+ "PNG_USER_CONFIG",
+ "CHROME_PNG_WRITE_SUPPORT"
+ "U_STATIC_IMPLEMENTATION",
+ "CERT_CHAIN_PARA_HAS_EXTRA_FIELDS",
+ "WIN32_LEAN_AND_MEAN",
+ ],
+ CCFLAGS = [
+ '/TP',
+
+ '/Wp64',
+
+ '/wd4503',
+ '/wd4819',
+ ],
+)
+
+input_files = [
+ "../../app/google_update_settings$OBJSUFFIX",
+ "copy_tree_work_item.cc",
+ "create_dir_work_item.cc",
+ "create_reg_key_work_item.cc",
+ "delete_tree_work_item.cc",
+ "google_update_constants.cc",
+ "helper.cc",
+ "install_util.cc",
+ "l10n_string_util.cc",
+ "logging_installer.cc",
+ "lzma_util.cc",
+ "set_reg_value_work_item.cc",
+ "shell_util.cc",
+ "util_constants.cc",
+ "version.cc",
+ "work_item.cc",
+ "work_item_list.cc",
+]
+
+x = env.StaticLibrary('util', input_files)
+
+
+# create_string_rc.py imports FP.py from the tools/grit/grit/extern
+# directory, so add that to PYTHONPATH for this command execution.
+env_x = env.Clone()
+env_x.AppendENVPath('PYTHONPATH', [Dir('#/../tools/grit/grit/extern').abspath])
+env_x.Command(['$CHROME_DIR/installer/util/setup_strings.rc',
+ '$CHROME_DIR/installer/util/setup_strings.h'],
+ ['$CHROME_DIR/installer/util/prebuild/create_string_rc.py',
+ '$CHROME_DIR/app/generated_resources.grd'] +
+ env.Glob('$CHROME_DIR/app/resources/*.xtb'),
+ "$PYTHON ${SOURCES[0]} ${TARGET.dir}")
diff --git a/chrome/installer/util/copy_tree_work_item.cc b/chrome/installer/util/copy_tree_work_item.cc new file mode 100644 index 0000000..c6eaacd --- /dev/null +++ b/chrome/installer/util/copy_tree_work_item.cc @@ -0,0 +1,169 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/installer/util/copy_tree_work_item.h" + +#include <shlwapi.h> +#include "base/file_util.h" +#include "chrome/installer/util/logging_installer.h" + +CopyTreeWorkItem::~CopyTreeWorkItem() { + if (file_util::PathExists(backup_path_)) { + file_util::Delete(backup_path_, true); + } +} + +CopyTreeWorkItem::CopyTreeWorkItem(std::wstring source_path, + std::wstring dest_path, + std::wstring temp_dir, + CopyOverWriteOption overwrite_option, + std::wstring alternative_path) + : source_path_(source_path), + dest_path_(dest_path), + temp_dir_(temp_dir), + overwrite_option_(overwrite_option), + alternative_path_(alternative_path), + copied_to_dest_path_(false), + moved_to_backup_(false), + copied_to_alternate_path_(false) { +} + +bool CopyTreeWorkItem::Do() { + if (!file_util::PathExists(source_path_)) { + LOG(ERROR) << source_path_ << " does not exist"; + return false; + } + + bool dest_exist = file_util::PathExists(dest_path_); + // handle overwrite_option_ = IF_DIFFERENT case + if ((dest_exist) && + (overwrite_option_ == WorkItem::IF_DIFFERENT) && // only for single file + (!PathIsDirectory(source_path_.c_str())) && + (!PathIsDirectory(dest_path_.c_str())) && + (file_util::ContentsEqual(source_path_, dest_path_))) { + LOG(INFO) << "Source file " << source_path_ + << " and destination file " << dest_path_ + << " are exactly same. Returning true."; + return true; + } + + // handle overwrite_option_ = RENAME_IF_IN_USE case + if ((dest_exist) && + (overwrite_option_ == WorkItem::RENAME_IF_IN_USE) && // only for a file + (!PathIsDirectory(source_path_.c_str())) && + (!PathIsDirectory(dest_path_.c_str())) && + (IsFileInUse(dest_path_))) { + if (alternative_path_.empty() || + file_util::PathExists(alternative_path_) || + !file_util::CopyFile(source_path_, alternative_path_)) { + LOG(ERROR) << "failed to copy " << source_path_ << + " to " << alternative_path_; + return false; + } else { + copied_to_alternate_path_ = true; + LOG(INFO) << "Copied source file " << source_path_ + << " to alternative path " << alternative_path_; + return true; + } + } + + // All other cases where we move dest if it exists, and copy the files + if (dest_exist) { + if (!GetBackupPath()) + return false; + + if (file_util::Move(dest_path_, backup_path_)) { + moved_to_backup_ = true; + LOG(INFO) << "Moved destination " << dest_path_ + << " to backup path " << backup_path_; + } else { + LOG(ERROR) << "failed moving " << dest_path_ << " to " << backup_path_; + return false; + } + } + + if (file_util::CopyDirectory(source_path_, dest_path_, true)) { + copied_to_dest_path_ = true; + LOG(INFO) << "Copied source " << source_path_ + << " to destination " << dest_path_; + } else { + LOG(ERROR) << "failed copy " << source_path_ << " to " << dest_path_; + return false; + } + + return true; +} + +void CopyTreeWorkItem::Rollback() { + // Normally the delete operations below should not fail unless some + // programs like anti-virus are inpecting the files we just copied. + // If this does happen sometimes, we may consider using Move instead of + // Delete here. For now we just log the error and continue with the + // rest of rollback operation. + if (copied_to_dest_path_ && !file_util::Delete(dest_path_, true)) { + LOG(ERROR) << "Can not delete " << dest_path_; + } + if (moved_to_backup_ && !file_util::Move(backup_path_, dest_path_)) { + LOG(ERROR) << "failed move " << backup_path_ << " to " << dest_path_; + } + if (copied_to_alternate_path_ && + !file_util::Delete(alternative_path_, true)) { + LOG(ERROR) << "Can not delete " << alternative_path_; + } +} + +bool CopyTreeWorkItem::IsFileInUse(std::wstring path) { + if (!file_util::PathExists(path)) + return false; + + HANDLE handle = ::CreateFile(path.c_str(), FILE_ALL_ACCESS, + NULL, NULL, OPEN_EXISTING, NULL, NULL); + if (handle == INVALID_HANDLE_VALUE) + return true; + + CloseHandle(handle); + return false; +} + +bool CopyTreeWorkItem::GetBackupPath() { + std::wstring file_name = file_util::GetFilenameFromPath(dest_path_); + backup_path_.assign(temp_dir_); + file_util::AppendToPath(&backup_path_, file_name); + + if (file_util::PathExists(backup_path_)) { + // Ideally we should not fail immediately. Instead we could try some + // random paths under temp_dir_ until we reach certain limit. + // For now our caller always provides a good temporary directory so + // we don't bother. + LOG(ERROR) << "backup path " << backup_path_ << " already exists"; + return false; + } + + return true; +} diff --git a/chrome/installer/util/copy_tree_work_item.h b/chrome/installer/util/copy_tree_work_item.h new file mode 100644 index 0000000..c942fe9 --- /dev/null +++ b/chrome/installer/util/copy_tree_work_item.h @@ -0,0 +1,105 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_INSTALLER_UTIL_COPY_TREE_WORK_ITEM_H__ +#define CHROME_INSTALLER_UTIL_COPY_TREE_WORK_ITEM_H__ + +#include <string> +#include <windows.h> +#include "chrome/installer/util/work_item.h" + +// A WorkItem subclass that recursively copies a file system hierarchy from +// source path to destination path. It also creates all necessary intermediate +// paths of the destination path if they do not exist. The file system +// hierarchy could be a single file, or a directory. +// Under the cover CopyTreeWorkItem moves the destination path, if existing, +// to the temporary directory passed in, and then copies the source hierarchy +// to the destination location. During rollback the original destination +// hierarchy is moved back. +class CopyTreeWorkItem : public WorkItem { + public: + virtual ~CopyTreeWorkItem(); + + virtual bool Do(); + + virtual void Rollback(); + + private: + friend class WorkItem; + + // See comments on corresponding member varibles for the semantics of + // arguments. + // Notes on temp_path: to facilitate rollback, the caller needs to supply + // a temporary directory to save the original files if they exist under + // dest_path. + CopyTreeWorkItem(std::wstring source_path, std::wstring dest_path, + std::wstring temp_dir, CopyOverWriteOption overwrite_option, + std::wstring alternative_path); + + // Checks if the path specified is in use (and hence can not be deleted) + bool IsFileInUse(std::wstring path); + + // Get a backup path that can keep the original files under dest_path_, + // and set backup_path_ with the result. + bool GetBackupPath(); + + // Source path to copy files from. + std::wstring source_path_; + + // Destination path to copy files to. + std::wstring dest_path_; + + // Temporary directory that can be used. + std::wstring temp_dir_; + + // Controls the behavior for overwriting. + CopyOverWriteOption overwrite_option_; + + // If overwrite_option_ = RENAME_IF_IN_USE, this variables stores the path + // to be used if the file is in use and hence we want to copy it to a + // different path. + std::wstring alternative_path_; + + // Whether the source was copied to dest_path_ + bool copied_to_dest_path_; + + // Whether the original files have been moved to backup path under + // temporary directory. If true, moving back is needed during rollback. + bool moved_to_backup_; + + // Whether the source was copied to alternative_path_ because dest_path_ + // existed and was in use. Needed during rollback. + bool copied_to_alternate_path_; + + // The full path in temporary directory that the original dest_path_ has + // been moved to. + std::wstring backup_path_; +}; + +#endif // CHROME_INSTALLER_UTIL_COPY_TREE_WORK_ITEM_H__ diff --git a/chrome/installer/util/copy_tree_work_item_unittest.cc b/chrome/installer/util/copy_tree_work_item_unittest.cc new file mode 100644 index 0000000..40e4a95 --- /dev/null +++ b/chrome/installer/util/copy_tree_work_item_unittest.cc @@ -0,0 +1,620 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include <fstream> +#include <iostream> + +#include "base/base_paths.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/process_util.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/installer/util/work_item.h" +#include "chrome/installer/util/copy_tree_work_item.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + class CopyTreeWorkItemTest : public testing::Test { + protected: + virtual void SetUp() { + // Name a subdirectory of the user temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + file_util::AppendToPath(&test_dir_, L"CopyTreeWorkItemTest"); + + // Create a fresh, empty copy of this test directory. + file_util::Delete(test_dir_, true); + CreateDirectory(test_dir_.c_str(), NULL); + + // Create a tempory directory under the test directory. + temp_dir_.assign(test_dir_); + file_util::AppendToPath(&temp_dir_, L"temp"); + CreateDirectory(temp_dir_.c_str(), NULL); + + // Create a log file + std::wstring log_file; + ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file)); + ASSERT_TRUE(file_util::PathExists(log_file)); + + logging::InitLogging(log_file.c_str(), + logging::LOG_ONLY_TO_FILE, + logging::LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE); + logging::SetMinLogLevel(logging::LOG_INFO); + + ASSERT_TRUE(file_util::PathExists(test_dir_)); + ASSERT_TRUE(file_util::PathExists(temp_dir_)); + } + + virtual void TearDown() { + logging::CloseLogFile(); + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, false)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // the path to temporary directory used to contain the test operations + std::wstring test_dir_; + std::wstring temp_dir_; + }; + + // Simple function to dump some text into a new file. + void CreateTextFile(const std::wstring& filename, + const std::wstring& contents) { + std::ofstream file; + file.open(filename.c_str()); + ASSERT_TRUE(file.is_open()); + file << contents; + file.close(); + } + + // Simple function to read text from a file. + std::wstring ReadTextFile(const std::wstring& filename) { + WCHAR contents[64]; + std::wifstream file; + file.open(filename.c_str()); + EXPECT_TRUE(file.is_open()); + file.getline(contents, 64); + file.close(); + return std::wstring(contents); + } + + wchar_t text_content_1[] = L"Gooooooooooooooooooooogle"; + wchar_t text_content_2[] = L"Overwrite Me"; +}; + +// Copy one file from source to destination. +TEST_F(CopyTreeWorkItemTest, CopyFile) { + // Create source file + std::wstring file_name_from(test_dir_); + file_util::AppendToPath(&file_name_from, L"File_From.txt"); + CreateTextFile(file_name_from, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Create destination path + std::wstring dir_name_to(test_dir_); + file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir"); + CreateDirectory(dir_name_to.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_to)); + + std::wstring file_name_to(dir_name_to); + file_util::AppendToPath(&file_name_to, L"File_To.txt"); + + // test Do() + scoped_ptr<CopyTreeWorkItem> work_item( + WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to, + temp_dir_, WorkItem::ALWAYS)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_TRUE(file_util::ContentsEqual(file_name_from, file_name_to)); + + // test rollback() + work_item->Rollback(); + + EXPECT_FALSE(file_util::PathExists(file_name_to)); + EXPECT_TRUE(file_util::PathExists(file_name_from)); +} + +// Copy one file, overwriting the existing one in destination. +// Test with always_overwrite being true or false. The file is overwritten +// regardless since the content at destination file is different from source. +TEST_F(CopyTreeWorkItemTest, CopyFileOverwrite) { + // Create source file + std::wstring file_name_from(test_dir_); + file_util::AppendToPath(&file_name_from, L"File_From.txt"); + CreateTextFile(file_name_from, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Create destination file + std::wstring dir_name_to(test_dir_); + file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir"); + CreateDirectory(dir_name_to.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_to)); + + std::wstring file_name_to(dir_name_to); + file_util::AppendToPath(&file_name_to, L"File_To.txt"); + CreateTextFile(file_name_to, text_content_2); + ASSERT_TRUE(file_util::PathExists(file_name_to)); + + // test Do() with always_overwrite being true. + scoped_ptr<CopyTreeWorkItem> work_item( + WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to, + temp_dir_, WorkItem::ALWAYS)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1)); + + // test rollback() + work_item->Rollback(); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_2)); + + // test Do() with always_overwrite being false. + // the file is still overwritten since the content is different. + work_item.reset( + WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to, + temp_dir_, WorkItem::IF_DIFFERENT)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1)); + + // test rollback() + work_item->Rollback(); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_2)); +} + +// Copy one file, with the existing one in destination having the same +// content. +// If always_overwrite being true, the file is overwritten. +// If always_overwrite being false, the file is unchanged. +TEST_F(CopyTreeWorkItemTest, CopyFileSameContent) { + // Create source file + std::wstring file_name_from(test_dir_); + file_util::AppendToPath(&file_name_from, L"File_From.txt"); + CreateTextFile(file_name_from, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Create destination file + std::wstring dir_name_to(test_dir_); + file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir"); + CreateDirectory(dir_name_to.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_to)); + + std::wstring file_name_to(dir_name_to); + file_util::AppendToPath(&file_name_to, L"File_To.txt"); + CreateTextFile(file_name_to, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_to)); + + // Get the path of backup file + std::wstring backup_file(temp_dir_); + file_util::AppendToPath(&backup_file, L"File_To.txt"); + + // test Do() with always_overwrite being true. + scoped_ptr<CopyTreeWorkItem> work_item( + WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to, + temp_dir_, WorkItem::ALWAYS)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1)); + // we verify the file is overwritten by checking the existence of backup + // file. + EXPECT_TRUE(file_util::PathExists(backup_file)); + EXPECT_EQ(0, ReadTextFile(backup_file).compare(text_content_1)); + + // test rollback() + work_item->Rollback(); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1)); + // the backup file should be gone after rollback + EXPECT_FALSE(file_util::PathExists(backup_file)); + + // test Do() with always_overwrite being false. nothing should change. + work_item.reset( + WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to, + temp_dir_, WorkItem::IF_DIFFERENT)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1)); + // we verify the file is not overwritten by checking that the backup + // file does not exist. + EXPECT_FALSE(file_util::PathExists(backup_file)); + + // test rollback(). nothing should happen here. + work_item->Rollback(); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1)); + EXPECT_FALSE(file_util::PathExists(backup_file)); +} + +// Copy one file and without rollback. Verify all temporary files are deleted. +TEST_F(CopyTreeWorkItemTest, CopyFileAndCleanup) { + // Create source file + std::wstring file_name_from(test_dir_); + file_util::AppendToPath(&file_name_from, L"File_From.txt"); + CreateTextFile(file_name_from, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Create destination file + std::wstring dir_name_to(test_dir_); + file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir"); + CreateDirectory(dir_name_to.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_to)); + + std::wstring file_name_to(dir_name_to); + file_util::AppendToPath(&file_name_to, L"File_To.txt"); + CreateTextFile(file_name_to, text_content_2); + ASSERT_TRUE(file_util::PathExists(file_name_to)); + + // Get the path of backup file + std::wstring backup_file(temp_dir_); + file_util::AppendToPath(&backup_file, L"File_To.txt"); + + { + // test Do(). + scoped_ptr<CopyTreeWorkItem> work_item( + WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to, + temp_dir_, WorkItem::IF_DIFFERENT)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1)); + // verify the file is moved to backup place. + EXPECT_TRUE(file_util::PathExists(backup_file)); + EXPECT_EQ(0, ReadTextFile(backup_file).compare(text_content_2)); + } + + // verify the backup file is cleaned up as well. + EXPECT_FALSE(file_util::PathExists(backup_file)); +} + +// Copy one file, with the existing one in destination being used with +// overwrite option as IF_DIFFERENT. This destination-file-in-use should +// be moved to backup location after Do() and moved back after Rollback(). +TEST_F(CopyTreeWorkItemTest, CopyFileInUse) { + // Create source file + std::wstring file_name_from(test_dir_); + file_util::AppendToPath(&file_name_from, L"File_From"); + CreateTextFile(file_name_from, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Create an executable in destination path by copying ourself to it. + wchar_t exe_full_path_str[MAX_PATH]; + ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH); + std::wstring exe_full_path(exe_full_path_str); + + std::wstring dir_name_to(test_dir_); + file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir"); + CreateDirectory(dir_name_to.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_to)); + + std::wstring file_name_to(dir_name_to); + file_util::AppendToPath(&file_name_to, L"File_To"); + file_util::CopyFile(exe_full_path, file_name_to); + ASSERT_TRUE(file_util::PathExists(file_name_to)); + + LOG(INFO) << "copy ourself from " << exe_full_path << " to " << file_name_to; + + // Run the executable in destination path + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi = {0}; + ASSERT_TRUE( + ::CreateProcessW(NULL, const_cast<wchar_t*>(file_name_to.c_str()), + NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED, + NULL, NULL, &si, &pi)); + + // Get the path of backup file + std::wstring backup_file(temp_dir_); + file_util::AppendToPath(&backup_file, L"File_To"); + + // test Do(). + scoped_ptr<CopyTreeWorkItem> work_item( + WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to, + temp_dir_, WorkItem::IF_DIFFERENT)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1)); + // verify the file in used is moved to backup place. + EXPECT_TRUE(file_util::PathExists(backup_file)); + EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, backup_file)); + + // test rollback() + work_item->Rollback(); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, file_name_to)); + // the backup file should be gone after rollback + EXPECT_FALSE(file_util::PathExists(backup_file)); + + TerminateProcess(pi.hProcess, 0); + // make sure the handle is closed. + WaitForSingleObject(pi.hProcess, INFINITE); +} + +// Test overwrite option RENAME_IF_IN_USE: +// 1. If destination file is in use, the source should be copied with the +// new name after Do() and this new name file should be deleted +// after rollback. +// 2. If destination file is not in use, the source should be copied in the +// destination folder after Do() and should be rolled back after Rollback(). +TEST_F(CopyTreeWorkItemTest, RenameAndCopyTest) { + // Create source file + std::wstring file_name_from(test_dir_); + file_util::AppendToPath(&file_name_from, L"File_From"); + CreateTextFile(file_name_from, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Create an executable in destination path by copying ourself to it. + wchar_t exe_full_path_str[MAX_PATH]; + ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH); + std::wstring exe_full_path(exe_full_path_str); + + std::wstring dir_name_to(test_dir_); + file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir"); + CreateDirectory(dir_name_to.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_to)); + + std::wstring file_name_to(dir_name_to), alternate_to(dir_name_to); + file_util::AppendToPath(&file_name_to, L"File_To"); + file_util::AppendToPath(&alternate_to, L"Alternate_To"); + file_util::CopyFile(exe_full_path, file_name_to); + ASSERT_TRUE(file_util::PathExists(file_name_to)); + + LOG(INFO) << "copy ourself from " << exe_full_path << " to " << file_name_to; + + // Run the executable in destination path + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi = {0}; + ASSERT_TRUE( + ::CreateProcessW(NULL, const_cast<wchar_t*>(file_name_to.c_str()), + NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED, + NULL, NULL, &si, &pi)); + + // Get the path of backup file + std::wstring backup_file(temp_dir_); + file_util::AppendToPath(&backup_file, L"File_To"); + + // test Do(). + scoped_ptr<CopyTreeWorkItem> work_item( + WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to, + temp_dir_, WorkItem::RENAME_IF_IN_USE, + alternate_to)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, file_name_to)); + // verify that the backup path does not exist + EXPECT_FALSE(file_util::PathExists(backup_file)); + EXPECT_TRUE(file_util::ContentsEqual(file_name_from, alternate_to)); + + // test rollback() + work_item->Rollback(); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, file_name_to)); + EXPECT_FALSE(file_util::PathExists(backup_file)); + // the alternate file should be gone after rollback + EXPECT_FALSE(file_util::PathExists(alternate_to)); + + TerminateProcess(pi.hProcess, 0); + // make sure the handle is closed. + WaitForSingleObject(pi.hProcess, INFINITE); + + // Now the process has terminated, lets try overwriting the file again + work_item.reset(WorkItem::CreateCopyTreeWorkItem( + file_name_from, file_name_to, temp_dir_, WorkItem::RENAME_IF_IN_USE, + alternate_to)); + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_TRUE(file_util::ContentsEqual(file_name_from, file_name_to)); + // verify that the backup path does exist + EXPECT_TRUE(file_util::PathExists(backup_file)); + EXPECT_FALSE(file_util::PathExists(alternate_to)); + + // test rollback() + work_item->Rollback(); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, file_name_to)); + // the backup file should be gone after rollback + EXPECT_FALSE(file_util::PathExists(backup_file)); + EXPECT_FALSE(file_util::PathExists(alternate_to)); +} + +// Copy one file without rollback. The existing one in destination is in use. +// Verify it is moved to backup location and stays there. +TEST_F(CopyTreeWorkItemTest, CopyFileInUseAndCleanup) { + // Create source file + std::wstring file_name_from(test_dir_); + file_util::AppendToPath(&file_name_from, L"File_From"); + CreateTextFile(file_name_from, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Create an executable in destination path by copying ourself to it. + wchar_t exe_full_path_str[MAX_PATH]; + ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH); + std::wstring exe_full_path(exe_full_path_str); + + std::wstring dir_name_to(test_dir_); + file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir"); + CreateDirectory(dir_name_to.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_to)); + + std::wstring file_name_to(dir_name_to); + file_util::AppendToPath(&file_name_to, L"File_To"); + file_util::CopyFile(exe_full_path, file_name_to); + ASSERT_TRUE(file_util::PathExists(file_name_to)); + + LOG(INFO) << "copy ourself from " << exe_full_path << " to " << file_name_to; + + // Run the executable in destination path + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi = {0}; + ASSERT_TRUE( + ::CreateProcessW(NULL, const_cast<wchar_t*>(file_name_to.c_str()), + NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED, + NULL, NULL, &si, &pi)); + + // Get the path of backup file + std::wstring backup_file(temp_dir_); + file_util::AppendToPath(&backup_file, L"File_To"); + + // test Do(). + { + scoped_ptr<CopyTreeWorkItem> work_item( + WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to, + temp_dir_, WorkItem::IF_DIFFERENT)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1)); + EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1)); + // verify the file in used is moved to backup place. + EXPECT_TRUE(file_util::PathExists(backup_file)); + EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, backup_file)); + } + + // verify the file in used should be still at the backup place. + EXPECT_TRUE(file_util::PathExists(backup_file)); + EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, backup_file)); + + TerminateProcess(pi.hProcess, 0); + // make sure the handle is closed. + WaitForSingleObject(pi.hProcess, INFINITE); +} + +// Copy a tree from source to destination. +TEST_F(CopyTreeWorkItemTest, CopyTree) { + // Create source tree + std::wstring dir_name_from(test_dir_); + file_util::AppendToPath(&dir_name_from, L"from"); + CreateDirectory(dir_name_from.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_from)); + + std::wstring dir_name_from_1(dir_name_from); + file_util::AppendToPath(&dir_name_from_1, L"1"); + CreateDirectory(dir_name_from_1.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_from_1)); + + std::wstring dir_name_from_2(dir_name_from); + file_util::AppendToPath(&dir_name_from_2, L"2"); + CreateDirectory(dir_name_from_2.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_from_2)); + + std::wstring file_name_from_1(dir_name_from_1); + file_util::AppendToPath(&file_name_from_1, L"File_1.txt"); + CreateTextFile(file_name_from_1, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_from_1)); + + std::wstring file_name_from_2(dir_name_from_2); + file_util::AppendToPath(&file_name_from_2, L"File_2.txt"); + CreateTextFile(file_name_from_2, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_from_2)); + + std::wstring dir_name_to(test_dir_); + file_util::AppendToPath(&dir_name_to, L"to"); + + // test Do() + { + scoped_ptr<CopyTreeWorkItem> work_item( + WorkItem::CreateCopyTreeWorkItem(dir_name_from, dir_name_to, + temp_dir_, WorkItem::ALWAYS)); + + EXPECT_TRUE(work_item->Do()); + } + + std::wstring file_name_to_1(dir_name_to); + file_util::AppendToPath(&file_name_to_1, L"1"); + file_util::AppendToPath(&file_name_to_1, L"File_1.txt"); + EXPECT_TRUE(file_util::PathExists(file_name_to_1)); + LOG(INFO) << "compare " << file_name_from_1 << " and " << file_name_to_1; + EXPECT_TRUE(file_util::ContentsEqual(file_name_from_1, file_name_to_1)); + + std::wstring file_name_to_2(dir_name_to); + file_util::AppendToPath(&file_name_to_2, L"2"); + file_util::AppendToPath(&file_name_to_2, L"File_2.txt"); + EXPECT_TRUE(file_util::PathExists(file_name_to_2)); + LOG(INFO) << "compare " << file_name_from_2 << " and " << file_name_to_2; + EXPECT_TRUE(file_util::ContentsEqual(file_name_from_2, file_name_to_2)); +} diff --git a/chrome/installer/util/create_dir_work_item.cc b/chrome/installer/util/create_dir_work_item.cc new file mode 100644 index 0000000..ba0f0a5 --- /dev/null +++ b/chrome/installer/util/create_dir_work_item.cc @@ -0,0 +1,94 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/file_util.h" +#include "chrome/installer/util/create_dir_work_item.h" +#include "chrome/installer/util/logging_installer.h" + +CreateDirWorkItem::~CreateDirWorkItem() { +} + +CreateDirWorkItem::CreateDirWorkItem(const std::wstring& path) + : path_(path), + rollback_needed_(false) { +} + +void CreateDirWorkItem::GetTopDirToCreate() { + if (file_util::PathExists(path_)) { + top_path_.clear(); + return; + } + + std::wstring parent_dir(path_); + do { + top_path_.assign(parent_dir); + file_util::UpOneDirectoryOrEmpty(&parent_dir); + } while (!parent_dir.empty() && !file_util::PathExists(parent_dir)); + return; +} + +bool CreateDirWorkItem::Do() { + LOG(INFO) << "creating directory " << path_; + GetTopDirToCreate(); + if (top_path_.empty()) + return true; + + LOG(INFO) << "top directory that needs to be created: " \ + << top_path_; + bool result = file_util::CreateDirectory(path_); + LOG(INFO) << "directory creation result: " << result; + + rollback_needed_ = true; + + return result; +} + +void CreateDirWorkItem::Rollback() { + if (!rollback_needed_) + return; + + // Delete all the directories we created to rollback. + // Note we can not recusively delete top_path_ since we don't want to + // delete non-empty directory. (We may have created a shared directory). + // Instead we walk through path_ to top_path_ and delete directories + // along the way. + std::wstring path_to_delete(path_); + + while(1) { + if (file_util::PathExists(path_to_delete)) { + if (!RemoveDirectory(path_to_delete.c_str())) + break; + } + if (!path_to_delete.compare(top_path_)) + break; + file_util::UpOneDirectoryOrEmpty(&path_to_delete); + } + + return; +} diff --git a/chrome/installer/util/create_dir_work_item.h b/chrome/installer/util/create_dir_work_item.h new file mode 100644 index 0000000..9d3971d --- /dev/null +++ b/chrome/installer/util/create_dir_work_item.h @@ -0,0 +1,70 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_INSTALLER_UTIL_CREATE_DIR_WORK_ITEM_H__ +#define CHROME_INSTALLER_UTIL_CREATE_DIR_WORK_ITEM_H__ + +#include <string> +#include <windows.h> +#include "chrome/installer/util/work_item.h" + +// A WorkItem subclass that creates a directory with the specified path. +// It also creates all necessary intermediate paths if they do not exist. +class CreateDirWorkItem : public WorkItem { + public: + virtual ~CreateDirWorkItem(); + + virtual bool Do(); + + // Rollback tries to remove all directories created along the path. + // If the leaf directory or one of the intermediate directories are not + // empty, the non-empty directory and its parent directories will not be + // removed. + virtual void Rollback(); + + private: + friend class WorkItem; + + CreateDirWorkItem(const std::wstring& path); + + // Get the top most directory that needs to be created in order to create + // "path_", and set "top_path_" accordingly. if "path_" already exists, + // "top_path_" is set to empty string. + void GetTopDirToCreate(); + + // Path of the directory to be created. + std::wstring path_; + + // The top most directory that needs to be created. + std::wstring top_path_; + + bool rollback_needed_; +}; + +#endif // CHROME_INSTALLER_UTIL_CREATE_DIR_WORK_ITEM_H__ diff --git a/chrome/installer/util/create_dir_work_item_unittest.cc b/chrome/installer/util/create_dir_work_item_unittest.cc new file mode 100644 index 0000000..d4ba9b2 --- /dev/null +++ b/chrome/installer/util/create_dir_work_item_unittest.cc @@ -0,0 +1,169 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include "base/base_paths.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/installer/util/work_item.h" +#include "chrome/installer/util/create_dir_work_item.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + class CreateDirWorkItemTest : public testing::Test { + protected: + virtual void SetUp() { + // Name a subdirectory of the temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + file_util::AppendToPath(&test_dir_, L"CreateDirWorkItemTest"); + + // Create a fresh, empty copy of this directory. + file_util::Delete(test_dir_, true); + CreateDirectory(test_dir_.c_str(), NULL); + } + virtual void TearDown() { + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, false)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // the path to temporary directory used to contain the test operations + std::wstring test_dir_; + }; +}; + +TEST_F(CreateDirWorkItemTest, CreatePath) { + std::wstring parent_dir(test_dir_); + file_util::AppendToPath(&parent_dir, L"a"); + CreateDirectory(parent_dir.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(parent_dir)); + + std::wstring top_dir_to_create(parent_dir); + file_util::AppendToPath(&top_dir_to_create, L"b"); + + std::wstring dir_to_create(top_dir_to_create); + file_util::AppendToPath(&dir_to_create, L"c"); + file_util::AppendToPath(&dir_to_create, L"d"); + + scoped_ptr<CreateDirWorkItem> work_item( + WorkItem::CreateCreateDirWorkItem(dir_to_create)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(dir_to_create)); + + work_item->Rollback(); + + // Rollback should delete all the paths up to top_dir_to_create. + EXPECT_FALSE(file_util::PathExists(top_dir_to_create)); + EXPECT_TRUE(file_util::PathExists(parent_dir)); +} + +TEST_F(CreateDirWorkItemTest, CreateExistingPath) { + std::wstring dir_to_create(test_dir_); + file_util::AppendToPath(&dir_to_create, L"aa"); + CreateDirectory(dir_to_create.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_to_create)); + + scoped_ptr<CreateDirWorkItem> work_item( + WorkItem::CreateCreateDirWorkItem(dir_to_create)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(dir_to_create)); + + work_item->Rollback(); + + // Rollback should not remove the path since it exists before + // the CreateDirWorkItem is called. + EXPECT_TRUE(file_util::PathExists(dir_to_create)); +} + +TEST_F(CreateDirWorkItemTest, CreateSharedPath) { + std::wstring dir_to_create_1(test_dir_); + file_util::AppendToPath(&dir_to_create_1, L"aaa"); + + std::wstring dir_to_create_2(dir_to_create_1); + file_util::AppendToPath(&dir_to_create_2, L"bbb"); + + std::wstring dir_to_create_3(dir_to_create_2); + file_util::AppendToPath(&dir_to_create_3, L"ccc"); + + scoped_ptr<CreateDirWorkItem> work_item( + WorkItem::CreateCreateDirWorkItem(dir_to_create_3)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(dir_to_create_3)); + + // Create another directory under dir_to_create_2 + std::wstring dir_to_create_4(dir_to_create_2); + file_util::AppendToPath(&dir_to_create_4, L"ddd"); + CreateDirectory(dir_to_create_4.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_to_create_4)); + + work_item->Rollback(); + + // Rollback should delete dir_to_create_3. + EXPECT_FALSE(file_util::PathExists(dir_to_create_3)); + + // Rollback should not delete dir_to_create_2 as it is shared. + EXPECT_TRUE(file_util::PathExists(dir_to_create_2)); + EXPECT_TRUE(file_util::PathExists(dir_to_create_4)); +} + +TEST_F(CreateDirWorkItemTest, RollbackWithMissingDir) { + std::wstring dir_to_create_1(test_dir_); + file_util::AppendToPath(&dir_to_create_1, L"aaaa"); + + std::wstring dir_to_create_2(dir_to_create_1); + file_util::AppendToPath(&dir_to_create_2, L"bbbb"); + + std::wstring dir_to_create_3(dir_to_create_2); + file_util::AppendToPath(&dir_to_create_3, L"cccc"); + + scoped_ptr<CreateDirWorkItem> work_item( + WorkItem::CreateCreateDirWorkItem(dir_to_create_3)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(file_util::PathExists(dir_to_create_3)); + + RemoveDirectory(dir_to_create_3.c_str()); + ASSERT_FALSE(file_util::PathExists(dir_to_create_3)); + + work_item->Rollback(); + + // dir_to_create_3 has already been deleted, Rollback should delete + // the rest. + EXPECT_FALSE(file_util::PathExists(dir_to_create_1)); +} diff --git a/chrome/installer/util/create_reg_key_work_item.cc b/chrome/installer/util/create_reg_key_work_item.cc new file mode 100644 index 0000000..719a4207a --- /dev/null +++ b/chrome/installer/util/create_reg_key_work_item.cc @@ -0,0 +1,131 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "shlwapi.h" + +#include "base/file_util.h" +#include "base/registry.h" +#include "chrome/installer/util/create_reg_key_work_item.h" +#include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/logging_installer.h" + +CreateRegKeyWorkItem::~CreateRegKeyWorkItem() { +} + +CreateRegKeyWorkItem::CreateRegKeyWorkItem(HKEY predefined_root, + std::wstring path) + : predefined_root_(predefined_root), + path_(path), + key_created_(false) { +} + +bool CreateRegKeyWorkItem::Do() { + if(!InitKeyList()) { + // Nothing needs to be done here. + LOG(INFO) << "no key to create"; + return true; + } + + RegKey key; + std::wstring key_path; + + // To create keys, we iterate from back to front. + for (size_t i = key_list_.size(); i > 0; i--) { + DWORD disposition; + key_path.assign(key_list_[i - 1]); + + if(key.CreateWithDisposition(predefined_root_, key_path.c_str(), + &disposition)) { + if (disposition == REG_OPENED_EXISTING_KEY) { + if (key_created_) { + // This should not happen. Someone created a subkey under the key + // we just created? + LOG(ERROR) << key_path << " exists, this is not expected."; + return false; + } + LOG(INFO) << key_path << " exists"; + // Remove the key path from list if it is already present. + key_list_.pop_back(); + } else if (disposition == REG_CREATED_NEW_KEY){ + LOG(INFO) << "created " << key_path; + key_created_ = true; + } else { + LOG(ERROR) << "unkown disposition"; + return false; + } + } else { + LOG(ERROR) << "fail to create " << key_path << " and the error is: " + << InstallUtil::FormatLastWin32Error(); + return false; + } + } + + return true; +} + +void CreateRegKeyWorkItem::Rollback() { + if (!key_created_) + return; + + std::wstring key_path; + // To delete keys, we iterate from front to back. + std::vector<std::wstring>::iterator itr; + for (itr = key_list_.begin(); itr != key_list_.end(); ++itr) { + key_path.assign(*itr); + if (SHDeleteEmptyKey(predefined_root_, key_path.c_str()) == + ERROR_SUCCESS) { + LOG(INFO) << "rollback: delete " << key_path; + } else { + LOG(INFO) << "rollback: can not delete " << key_path; + // The key might have been deleted, but we don't reliably know what + // error code(s) are returned in this case. So we just keep tring delete + // the rest. + } + } + + key_created_ = false; + key_list_.clear(); + return; +} + +bool CreateRegKeyWorkItem::InitKeyList() { + if (path_.empty()) + return false; + + std::wstring key_path(path_); + + do { + key_list_.push_back(key_path); + // This is pure string operation so it does not matter whether the + // path is file path or registry path. + file_util::UpOneDirectoryOrEmpty(&key_path); + } while(!key_path.empty()); + + return true; +} diff --git a/chrome/installer/util/create_reg_key_work_item.h b/chrome/installer/util/create_reg_key_work_item.h new file mode 100644 index 0000000..ce2b272 --- /dev/null +++ b/chrome/installer/util/create_reg_key_work_item.h @@ -0,0 +1,74 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_INSTALLER_UTIL_CREATE_REG_KEY_WORK_ITEM_H__ +#define CHROME_INSTALLER_UTIL_CREATE_REG_KEY_WORK_ITEM_H__ + +#include <string> +#include <vector> + +#include <windows.h> + +#include "chrome/installer/util/work_item.h" + +// A WorkItem subclass that creates a registry key at the given path. +// It also creates all necessary intermediate keys if they do not exist. +class CreateRegKeyWorkItem : public WorkItem { + public: + virtual ~CreateRegKeyWorkItem(); + + virtual bool Do(); + + virtual void Rollback(); + + private: + friend class WorkItem; + + CreateRegKeyWorkItem(HKEY predefined_root, std::wstring path); + + // Initialize key_list_ by adding all paths of keys from predefined_root_ + // to path_. Returns true if key_list_ is non empty. + bool InitKeyList(); + + // Root key under which we create the new key. The root key can only be + // one of the predefined keys on Windows. + HKEY predefined_root_; + + // Path of the key to be created. + std::wstring path_; + + // List of paths to all keys that need to be created from predefined_root_ + // to path_. + std::vector<std::wstring> key_list_; + + // Whether any key has been created. + bool key_created_; +}; + +#endif // CHROME_INSTALLER_UTIL_CREATE_REG_KEY_WORK_ITEM_H__ diff --git a/chrome/installer/util/create_reg_key_work_item_unittest.cc b/chrome/installer/util/create_reg_key_work_item_unittest.cc new file mode 100644 index 0000000..e694491 --- /dev/null +++ b/chrome/installer/util/create_reg_key_work_item_unittest.cc @@ -0,0 +1,212 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/registry.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/installer/util/create_reg_key_work_item.h" +#include "chrome/installer/util/work_item.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + wchar_t test_root[] = L"TmpTmp"; + class CreateRegKeyWorkItemTest : public testing::Test { + protected: + virtual void SetUp() { + // Create a temporary key for testing + RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); + key.DeleteKey(test_root); + ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, test_root, KEY_READ)); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, test_root, KEY_READ)); + + // Create a log file + std::wstring log_file; + ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file)); + ASSERT_TRUE(file_util::PathExists(log_file)); + + logging::InitLogging(log_file.c_str(), + logging::LOG_ONLY_TO_FILE, + logging::LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE); + logging::SetMinLogLevel(logging::LOG_INFO); + } + virtual void TearDown() { + logging::CloseLogFile(); + // Clean up the temporary key + RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); + ASSERT_TRUE(key.DeleteKey(test_root)); + } + }; +}; + +TEST_F(CreateRegKeyWorkItemTest, CreateKey) { + RegKey key; + + std::wstring parent_key(test_root); + file_util::AppendToPath(&parent_key, L"a"); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(), KEY_READ)); + + std::wstring top_key_to_create(parent_key); + file_util::AppendToPath(&top_key_to_create, L"b"); + + std::wstring key_to_create(top_key_to_create); + file_util::AppendToPath(&key_to_create, L"c"); + file_util::AppendToPath(&key_to_create, L"d"); + + scoped_ptr<CreateRegKeyWorkItem> work_item( + WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ)); + + work_item->Rollback(); + + // Rollback should delete all the keys up to top_key_to_create. + EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, top_key_to_create.c_str(), + KEY_READ)); + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, parent_key.c_str(), KEY_READ)); +} + +TEST_F(CreateRegKeyWorkItemTest, CreateExistingKey) { + RegKey key; + + std::wstring key_to_create(test_root); + file_util::AppendToPath(&key_to_create, L"aa"); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ)); + + scoped_ptr<CreateRegKeyWorkItem> work_item( + WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ)); + + work_item->Rollback(); + + // Rollback should not remove the key since it exists before + // the CreateRegKeyWorkItem is called. + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ)); +} + +TEST_F(CreateRegKeyWorkItemTest, CreateSharedKey) { + RegKey key; + std::wstring key_to_create_1(test_root); + file_util::AppendToPath(&key_to_create_1, L"aaa"); + + std::wstring key_to_create_2(key_to_create_1); + file_util::AppendToPath(&key_to_create_2, L"bbb"); + + std::wstring key_to_create_3(key_to_create_2); + file_util::AppendToPath(&key_to_create_3, L"ccc"); + + scoped_ptr<CreateRegKeyWorkItem> work_item( + WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, + key_to_create_3)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create_3.c_str(), KEY_READ)); + + // Create another key under key_to_create_2 + std::wstring key_to_create_4(key_to_create_2); + file_util::AppendToPath(&key_to_create_4, L"ddd"); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, key_to_create_4.c_str(), + KEY_READ)); + + work_item->Rollback(); + + // Rollback should delete key_to_create_3. + EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create_3.c_str(), KEY_READ)); + + // Rollback should not delete key_to_create_2 as it is shared. + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create_2.c_str(), KEY_READ)); + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create_4.c_str(), KEY_READ)); +} + +TEST_F(CreateRegKeyWorkItemTest, RollbackWithMissingKey) { + RegKey key; + std::wstring key_to_create_1(test_root); + file_util::AppendToPath(&key_to_create_1, L"aaaa"); + + std::wstring key_to_create_2(key_to_create_1); + file_util::AppendToPath(&key_to_create_2, L"bbbb"); + + std::wstring key_to_create_3(key_to_create_2); + file_util::AppendToPath(&key_to_create_3, L"cccc"); + + scoped_ptr<CreateRegKeyWorkItem> work_item( + WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, + key_to_create_3)); + + EXPECT_TRUE(work_item->Do()); + + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create_3.c_str(), KEY_READ)); + key.Close(); + + // now delete key_to_create_3 + ASSERT_TRUE(RegDeleteKey(HKEY_CURRENT_USER, key_to_create_3.c_str()) == + ERROR_SUCCESS); + ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create_3.c_str(), + KEY_READ)); + + work_item->Rollback(); + + // key_to_create_3 has already been deleted, Rollback should delete + // the rest. + ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create_1.c_str(), + KEY_READ)); +} + +TEST_F(CreateRegKeyWorkItemTest, RollbackWithSetValue) { + RegKey key; + + std::wstring key_to_create(test_root); + file_util::AppendToPath(&key_to_create, L"aaaaa"); + + scoped_ptr<CreateRegKeyWorkItem> work_item( + WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create)); + + EXPECT_TRUE(work_item->Do()); + + // Write a value under the key we just created. + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), + KEY_READ | KEY_SET_VALUE)); + EXPECT_TRUE(key.WriteValue(L"name", L"value")); + key.Close(); + + work_item->Rollback(); + + // Rollback should not remove the key. + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ)); +} diff --git a/chrome/installer/util/delete_tree_work_item.cc b/chrome/installer/util/delete_tree_work_item.cc new file mode 100644 index 0000000..8a96915 --- /dev/null +++ b/chrome/installer/util/delete_tree_work_item.cc @@ -0,0 +1,97 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/file_util.h" +#include "base/logging.h" +#include "chrome/installer/util/delete_tree_work_item.h" + +DeleteTreeWorkItem::~DeleteTreeWorkItem() { + std::wstring tmp_dir = file_util::GetDirectoryFromPath(backup_path_); + if (file_util::PathExists(tmp_dir)) { + file_util::Delete(tmp_dir, true); + } + tmp_dir = file_util::GetDirectoryFromPath(key_backup_path_); + if (file_util::PathExists(tmp_dir)) { + file_util::Delete(tmp_dir, true); + } +} + +DeleteTreeWorkItem::DeleteTreeWorkItem(std::wstring root_path, + std::wstring key_path) + : root_path_(root_path), + key_path_(key_path) { +} + +// We first try to move key_path_ to backup_path. If it succeeds, we go ahead +// and move the rest. +bool DeleteTreeWorkItem::Do() { + if (!key_path_.empty() && file_util::PathExists(key_path_)) { + if (!GetBackupPath(key_path_, &key_backup_path_) || + !file_util::CopyDirectory(key_path_, key_backup_path_, true) || + !file_util::Delete(key_path_, true)) { + LOG(ERROR) << "can not delete " << key_path_ + << " OR copy it to backup path " << key_backup_path_; + return false; + } + } + + if (!root_path_.empty() && file_util::PathExists(root_path_)) { + if (!GetBackupPath(root_path_, &backup_path_) || + !file_util::CopyDirectory(root_path_, backup_path_, true) || + !file_util::Delete(root_path_, true)) { + LOG(ERROR) << "can not delete " << root_path_ + << " OR copy it to backup path " << backup_path_; + return false; + } + } + return true; +} + +// If there are files in backup paths move them back. +void DeleteTreeWorkItem::Rollback() { + if (!backup_path_.empty() && file_util::PathExists(backup_path_)) { + file_util::Move(backup_path_, root_path_); + } + if (!key_backup_path_.empty() && file_util::PathExists(key_backup_path_)) { + file_util::Move(key_backup_path_, key_path_); + } +} + +bool DeleteTreeWorkItem::GetBackupPath(std::wstring for_path, + std::wstring* backup_path) { + if (!file_util::CreateNewTempDirectory(L"", backup_path)) { + // We assume that CreateNewTempDirectory() is doing its job well. + LOG(ERROR) << "Couldn't get backup path for delete."; + return false; + } + std::wstring file_name = file_util::GetFilenameFromPath(for_path); + file_util::AppendToPath(backup_path, file_name); + + return true; +} diff --git a/chrome/installer/util/delete_tree_work_item.h b/chrome/installer/util/delete_tree_work_item.h new file mode 100644 index 0000000..e480eec --- /dev/null +++ b/chrome/installer/util/delete_tree_work_item.h @@ -0,0 +1,74 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_INSTALLER_UTIL_DELETE_TREE_WORK_ITEM_H__ +#define CHROME_INSTALLER_UTIL_DELETE_TREE_WORK_ITEM_H__ + +#include <string> +#include <windows.h> +#include "chrome/installer/util/work_item.h" + +// A WorkItem subclass that recursively deletes a file system hierarchy at the +// given root path. The file system hierarchy could be a single file, or a +// directory. +// The file system hierarchy to be deleted can have a key file. If the key file +// is specified, deletion will be performed only if the key file is not in use. +class DeleteTreeWorkItem : public WorkItem { + public: + virtual ~DeleteTreeWorkItem(); + + virtual bool Do(); + + virtual void Rollback(); + + private: + friend class WorkItem; + + // Get a backup path that can keep root_path_ or key_path_ + bool GetBackupPath(std::wstring for_path, std::wstring* backup_path); + + DeleteTreeWorkItem(std::wstring root_path, std::wstring key_path); + + // Root path to delete. + std::wstring root_path_; + + // Path to the key file. If the key file is specified, deletion will be + // performed only if the key file is not in use. + std::wstring key_path_; + + // The full path in temporary directory that the original root_path_ has + // been moved to. + std::wstring backup_path_; + + // The full path in temporary directory that the original key_path_ has + // been moved to. + std::wstring key_backup_path_; +}; + +#endif // CHROME_INSTALLER_UTIL_DELETE_TREE_WORK_ITEM_H__ diff --git a/chrome/installer/util/delete_tree_work_item_unittest.cc b/chrome/installer/util/delete_tree_work_item_unittest.cc new file mode 100644 index 0000000..a055846 --- /dev/null +++ b/chrome/installer/util/delete_tree_work_item_unittest.cc @@ -0,0 +1,243 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include <fstream> +#include <iostream> + +#include "base/base_paths.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/process_util.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/installer/util/delete_tree_work_item.h" +#include "chrome/installer/util/work_item.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + class DeleteTreeWorkItemTest : public testing::Test { + protected: + virtual void SetUp() { + // Name a subdirectory of the user temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + file_util::AppendToPath(&test_dir_, L"DeleteTreeWorkItemTest"); + + // Create a fresh, empty copy of this test directory. + file_util::Delete(test_dir_, true); + CreateDirectory(test_dir_.c_str(), NULL); + + ASSERT_TRUE(file_util::PathExists(test_dir_)); + } + + virtual void TearDown() { + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, false)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // the path to temporary directory used to contain the test operations + std::wstring test_dir_; + }; + + // Simple function to dump some text into a new file. + void CreateTextFile(const std::wstring& filename, + const std::wstring& contents) { + std::ofstream file; + file.open(filename.c_str()); + ASSERT_TRUE(file.is_open()); + file << contents; + file.close(); + } + + wchar_t text_content_1[] = L"delete me"; + wchar_t text_content_2[] = L"delete me as well"; +}; + +// Delete a tree without key path. Everything should be deleted. +TEST_F(DeleteTreeWorkItemTest, DeleteTreeNoKeyPath) { + // Create tree to be deleted + std::wstring dir_name_delete(test_dir_); + file_util::AppendToPath(&dir_name_delete, L"to_be_delete"); + CreateDirectory(dir_name_delete.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_delete)); + + std::wstring dir_name_delete_1(dir_name_delete); + file_util::AppendToPath(&dir_name_delete_1, L"1"); + CreateDirectory(dir_name_delete_1.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_delete_1)); + + std::wstring dir_name_delete_2(dir_name_delete); + file_util::AppendToPath(&dir_name_delete_2, L"2"); + CreateDirectory(dir_name_delete_2.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_delete_2)); + + std::wstring file_name_delete_1(dir_name_delete_1); + file_util::AppendToPath(&file_name_delete_1, L"File_1.txt"); + CreateTextFile(file_name_delete_1, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_delete_1)); + + std::wstring file_name_delete_2(dir_name_delete_2); + file_util::AppendToPath(&file_name_delete_2, L"File_2.txt"); + CreateTextFile(file_name_delete_2, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_delete_2)); + + // test Do() + scoped_ptr<DeleteTreeWorkItem> work_item( + WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, std::wstring())); + EXPECT_TRUE(work_item->Do()); + + // everything should be gone + EXPECT_FALSE(file_util::PathExists(file_name_delete_1)); + EXPECT_FALSE(file_util::PathExists(file_name_delete_2)); + EXPECT_FALSE(file_util::PathExists(dir_name_delete)); + + work_item->Rollback(); + // everything should come back + EXPECT_TRUE(file_util::PathExists(file_name_delete_1)); + EXPECT_TRUE(file_util::PathExists(file_name_delete_2)); + EXPECT_TRUE(file_util::PathExists(dir_name_delete)); +} + + +// Delete a tree with keypath but not in use. Everything should be gone. +// Rollback should bring back everything +TEST_F(DeleteTreeWorkItemTest, DeleteTree) { + // Create tree to be deleted + std::wstring dir_name_delete(test_dir_); + file_util::AppendToPath(&dir_name_delete, L"to_be_delete"); + CreateDirectory(dir_name_delete.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_delete)); + + std::wstring dir_name_delete_1(dir_name_delete); + file_util::AppendToPath(&dir_name_delete_1, L"1"); + CreateDirectory(dir_name_delete_1.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_delete_1)); + + std::wstring dir_name_delete_2(dir_name_delete); + file_util::AppendToPath(&dir_name_delete_2, L"2"); + CreateDirectory(dir_name_delete_2.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_delete_2)); + + std::wstring file_name_delete_1(dir_name_delete_1); + file_util::AppendToPath(&file_name_delete_1, L"File_1.txt"); + CreateTextFile(file_name_delete_1, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_delete_1)); + + std::wstring file_name_delete_2(dir_name_delete_2); + file_util::AppendToPath(&file_name_delete_2, L"File_2.txt"); + CreateTextFile(file_name_delete_2, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_delete_2)); + + // test Do() + scoped_ptr<DeleteTreeWorkItem> work_item( + WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, file_name_delete_1)); + EXPECT_TRUE(work_item->Do()); + + // everything should be gone + EXPECT_FALSE(file_util::PathExists(file_name_delete_1)); + EXPECT_FALSE(file_util::PathExists(file_name_delete_2)); + EXPECT_FALSE(file_util::PathExists(dir_name_delete)); + + work_item->Rollback(); + // everything should come back + EXPECT_TRUE(file_util::PathExists(file_name_delete_1)); + EXPECT_TRUE(file_util::PathExists(file_name_delete_2)); + EXPECT_TRUE(file_util::PathExists(dir_name_delete)); +} + +// Delete a tree with key_path in use. Everything should still be there. +TEST_F(DeleteTreeWorkItemTest, DeleteTreeInUse) { + // Create tree to be deleted + std::wstring dir_name_delete(test_dir_); + file_util::AppendToPath(&dir_name_delete, L"to_be_delete"); + CreateDirectory(dir_name_delete.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_delete)); + + std::wstring dir_name_delete_1(dir_name_delete); + file_util::AppendToPath(&dir_name_delete_1, L"1"); + CreateDirectory(dir_name_delete_1.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_delete_1)); + + std::wstring dir_name_delete_2(dir_name_delete); + file_util::AppendToPath(&dir_name_delete_2, L"2"); + CreateDirectory(dir_name_delete_2.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(dir_name_delete_2)); + + std::wstring file_name_delete_1(dir_name_delete_1); + file_util::AppendToPath(&file_name_delete_1, L"File_1.txt"); + CreateTextFile(file_name_delete_1, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_delete_1)); + + std::wstring file_name_delete_2(dir_name_delete_2); + file_util::AppendToPath(&file_name_delete_2, L"File_2.txt"); + CreateTextFile(file_name_delete_2, text_content_1); + ASSERT_TRUE(file_util::PathExists(file_name_delete_2)); + + // Create a key path file. + std::wstring key_path(dir_name_delete); + file_util::AppendToPath(&key_path, L"key_file.exe"); + + wchar_t exe_full_path_str[MAX_PATH]; + ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH); + std::wstring exe_full_path(exe_full_path_str); + + file_util::CopyFile(exe_full_path, key_path); + ASSERT_TRUE(file_util::PathExists(key_path)); + + LOG(INFO) << "copy ourself from " << exe_full_path << " to " << key_path; + + // Run the key path file to keep it in use. + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi = {0}; + ASSERT_TRUE( + ::CreateProcessW(NULL, const_cast<wchar_t*>(key_path.c_str()), + NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED, + NULL, NULL, &si, &pi)); + + // test Do(). + { + scoped_ptr<DeleteTreeWorkItem> work_item( + WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, key_path)); + + // delete should fail as file in use. + EXPECT_FALSE(work_item->Do()); + } + + // verify everything is still there. + EXPECT_TRUE(file_util::PathExists(key_path)); + EXPECT_TRUE(file_util::PathExists(file_name_delete_1)); + EXPECT_TRUE(file_util::PathExists(file_name_delete_2)); + + TerminateProcess(pi.hProcess, 0); + // make sure the handle is closed. + WaitForSingleObject(pi.hProcess, INFINITE); +} diff --git a/chrome/installer/util/google_update_constants.cc b/chrome/installer/util/google_update_constants.cc new file mode 100644 index 0000000..8d89aa7 --- /dev/null +++ b/chrome/installer/util/google_update_constants.cc @@ -0,0 +1,43 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/installer/util/google_update_constants.h" + +namespace google_update { + +const wchar_t kChromeGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; +const wchar_t kGearsUpgradeCode[] = L"{D92DBAED-3E3E-4530-B30D-072D16C7DDD0}"; +const wchar_t kRegPathClients[] = L"Software\\Google\\Update\\Clients"; +const wchar_t kRegPathClientState[] = L"Software\\Google\\Update\\ClientState"; +const wchar_t kRegApFieldName[] = L"ap"; +const wchar_t kRegNameField[] = L"name"; +const wchar_t kRegVersionField[] = L"pv"; +const wchar_t kRegLastCheckedField[] = L"LastChecked"; + +} // namespace installer diff --git a/chrome/installer/util/google_update_constants.h b/chrome/installer/util/google_update_constants.h new file mode 100644 index 0000000..984749f --- /dev/null +++ b/chrome/installer/util/google_update_constants.h @@ -0,0 +1,52 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Defines all the command-line switches used with Google Update. + +#ifndef CHROME_INSTALLER_UTIL_GOOGLE_UPDATE_CONSTANTS_H__ +#define CHROME_INSTALLER_UTIL_GOOGLE_UPDATE_CONSTANTS_H__ + +namespace google_update { + +extern const wchar_t kChromeGuid[]; + +// Strictly speaking Google Update doesn't care about this GUID but it is still +// related to install as it is used by MSI to identify Gears. +extern const wchar_t kGearsUpgradeCode[]; + +extern const wchar_t kRegPathClients[]; +extern const wchar_t kRegPathClientState[]; +extern const wchar_t kRegApFieldName[]; +extern const wchar_t kRegNameField[]; +extern const wchar_t kRegVersionField[]; +extern const wchar_t kRegLastCheckedField[]; + +} // namespace google_update + +#endif // CHROME_INSTALLER_UTIL_GOOGLE_UPDATE_CONSTANTS_H__ diff --git a/chrome/installer/util/helper.cc b/chrome/installer/util/helper.cc new file mode 100644 index 0000000..271fdb2 --- /dev/null +++ b/chrome/installer/util/helper.cc @@ -0,0 +1,147 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/process_util.h" +#include "base/scoped_ptr.h" +#include "chrome/installer/util/delete_tree_work_item.h" +#include "chrome/installer/util/helper.h" +#include "chrome/installer/util/logging_installer.h" +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/version.h" +#include "chrome/installer/util/work_item_list.h" + +std::wstring installer::GetChromeInstallPath(bool system_install) { + std::wstring install_path; + if (system_install) { + PathService::Get(base::DIR_PROGRAM_FILES, &install_path); + } else { + PathService::Get(base::DIR_LOCAL_APP_DATA, &install_path); + } + + if (!install_path.empty()) { + file_util::AppendToPath(&install_path, + std::wstring(installer_util::kInstallGoogleDir)); + file_util::AppendToPath(&install_path, + std::wstring(installer_util::kChrome)); + file_util::AppendToPath(&install_path, + std::wstring(installer_util::kInstallBinaryDir)); + } + + return install_path; +} + +bool installer::LaunchChrome(bool system_install) { + std::wstring chrome_exe(L"\""); + chrome_exe.append(installer::GetChromeInstallPath(system_install)); + file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); + chrome_exe.append(L"\""); + return process_util::LaunchApp(chrome_exe, false, false, NULL); +} + +bool installer::LaunchChromeAndWaitForResult(bool system_install, + const std::wstring& options, + int32 timeout_ms, + int32* exit_code, + bool* is_timeout) { + std::wstring chrome_exe(installer::GetChromeInstallPath(system_install)); + if (chrome_exe.empty()) + return false; + file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); + + std::wstring command_line(chrome_exe); + command_line.append(options); + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi = {0}; + if (!::CreateProcessW(chrome_exe.c_str(), + const_cast<wchar_t*>(command_line.c_str()), + NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, + &si, &pi)) { + return false; + } + + DWORD wr = ::WaitForSingleObject(pi.hProcess, timeout_ms); + if (WAIT_TIMEOUT == wr) { + if (is_timeout) + *is_timeout = true; + } else { // WAIT_OBJECT_0 + if (is_timeout) + *is_timeout = false; + if (exit_code) { + ::GetExitCodeProcess(pi.hProcess, reinterpret_cast<DWORD*>(exit_code)); + } + } + + ::CloseHandle(pi.hProcess); + ::CloseHandle(pi.hThread); + return true; +} + +void installer::RemoveOldVersionDirs(const std::wstring& chrome_path, + const std::wstring& latest_version_str) { + std::wstring search_path(chrome_path); + file_util::AppendToPath(&search_path, L"*"); + + WIN32_FIND_DATA find_file_data; + HANDLE file_handle = FindFirstFile(search_path.c_str(), &find_file_data); + if (file_handle == INVALID_HANDLE_VALUE) + return; + + BOOL ret = TRUE; + scoped_ptr<installer::Version> version; + scoped_ptr<installer::Version> latest_version( + installer::Version::GetVersionFromString(latest_version_str)); + + // We try to delete all directories whose versions are lower than + // latest_version. + while (ret) { + if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + LOG(INFO) << "directory found: " << find_file_data.cFileName; + version.reset( + installer::Version::GetVersionFromString(find_file_data.cFileName)); + if (version.get() && latest_version->IsHigherThan(version.get())) { + std::wstring remove_dir(chrome_path); + file_util::AppendToPath(&remove_dir, find_file_data.cFileName); + std::wstring chrome_dll_path(remove_dir); + file_util::AppendToPath(&chrome_dll_path, installer_util::kChromeDll); + LOG(INFO) << "deleting directory " << remove_dir; + scoped_ptr<DeleteTreeWorkItem> item; + item.reset(WorkItem::CreateDeleteTreeWorkItem(remove_dir, + chrome_dll_path)); + item->Do(); + } + } + ret = FindNextFile(file_handle, &find_file_data); + } + + FindClose(file_handle); +} diff --git a/chrome/installer/util/helper.h b/chrome/installer/util/helper.h new file mode 100644 index 0000000..68e5115 --- /dev/null +++ b/chrome/installer/util/helper.h @@ -0,0 +1,72 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file contains helper functions used by setup. + +#ifndef CHROME_INSTALLER_UTIL_HELPER_H__ +#define CHROME_INSTALLER_UTIL_HELPER_H__ + +#include <string> + +namespace installer { + +// This function returns the install path for Chrome depending on whether its +// system wide install or user specific install. +// system_install: if true, the function returns system wide location +// (ProgramFiles\Google). Otherwise it returns user specific +// location (Document And Settings\<user>\Local Settings...) +std::wstring GetChromeInstallPath(bool system_install); + +// Launches Chrome without waiting for its exit. +bool LaunchChrome(bool system_install); + +// Launches Chrome with given command line, waits for Chrome to terminate +// within timeout_ms, and gets the process exit code if available. +// The function returns true as long as Chrome is successfully launched. +// The status of Chrome at the return of the function is given by exit_code +// and is_timeout. +bool LaunchChromeAndWaitForResult(bool system_install, + const std::wstring& options, + int32 timeout_ms, + int32* exit_code, + bool* is_timeout); + +// This function tries to remove all previous version directories after a new +// Chrome update. If an instance of Chrome with older version is still running +// on the system, its corresponding version directory will be left intact. +// (The version directory is subject for removal again during next update.) +// +// chrome_path: the root path of Chrome installation. +// latest_version_str: the latest version of Chrome installed. +void RemoveOldVersionDirs(const std::wstring& chrome_path, + const std::wstring& latest_version_str); + +} // namespace installer + +#endif diff --git a/chrome/installer/util/helper_unittest.cc b/chrome/installer/util/helper_unittest.cc new file mode 100644 index 0000000..be6b183 --- /dev/null +++ b/chrome/installer/util/helper_unittest.cc @@ -0,0 +1,231 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include <fstream> +#include <iostream> + +#include "base/base_paths.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/process_util.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/installer/util/helper.h" +#include "chrome/installer/util/work_item.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + class SetupHelperTest : public testing::Test { + protected: + virtual void SetUp() { + // Name a subdirectory of the user temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + file_util::AppendToPath(&test_dir_, L"SetupHelperTest"); + + // Create a fresh, empty copy of this test directory. + file_util::Delete(test_dir_, true); + CreateDirectory(test_dir_.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(test_dir_)); + + // Create a log file + std::wstring log_file; + ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file)); + ASSERT_TRUE(file_util::PathExists(log_file)); + + logging::InitLogging(log_file.c_str(), + logging::LOG_ONLY_TO_FILE, + logging::LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE); + logging::SetMinLogLevel(logging::LOG_INFO); + } + + virtual void TearDown() { + logging::CloseLogFile(); + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, false)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // the path to temporary directory used to contain the test operations + std::wstring test_dir_; + }; + + // Simple function to dump some text into a new file. + void CreateTextFile(const std::wstring& filename, + const std::wstring& contents) { + std::ofstream file; + file.open(filename.c_str()); + ASSERT_TRUE(file.is_open()); + file << contents; + file.close(); + } + + wchar_t text_content_1[] = L"delete me"; + wchar_t text_content_2[] = L"delete me as well"; +}; + +// Delete version directories. Everything lower than the given version +// should be deleted. +TEST_F(SetupHelperTest, Delete) { + // Create a Chrome dir + std::wstring chrome_dir(test_dir_); + file_util::AppendToPath(&chrome_dir, L"chrome"); + CreateDirectory(chrome_dir.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir)); + + std::wstring chrome_dir_1(chrome_dir); + file_util::AppendToPath(&chrome_dir_1, L"1.0.1.0"); + CreateDirectory(chrome_dir_1.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir_1)); + + std::wstring chrome_dir_2(chrome_dir); + file_util::AppendToPath(&chrome_dir_2, L"1.0.2.0"); + CreateDirectory(chrome_dir_2.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir_2)); + + std::wstring chrome_dir_3(chrome_dir); + file_util::AppendToPath(&chrome_dir_3, L"1.0.3.0"); + CreateDirectory(chrome_dir_3.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir_3)); + + std::wstring chrome_dir_4(chrome_dir); + file_util::AppendToPath(&chrome_dir_4, L"1.0.4.0"); + CreateDirectory(chrome_dir_4.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir_4)); + + std::wstring chrome_dll_1(chrome_dir_1); + file_util::AppendToPath(&chrome_dll_1, L"chrome.dll"); + CreateTextFile(chrome_dll_1, text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_1)); + + std::wstring chrome_dll_2(chrome_dir_2); + file_util::AppendToPath(&chrome_dll_2, L"chrome.dll"); + CreateTextFile(chrome_dll_2, text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_2)); + + std::wstring chrome_dll_3(chrome_dir_3); + file_util::AppendToPath(&chrome_dll_3, L"chrome.dll"); + CreateTextFile(chrome_dll_3, text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_3)); + + std::wstring chrome_dll_4(chrome_dir_4); + file_util::AppendToPath(&chrome_dll_4, L"chrome.dll"); + CreateTextFile(chrome_dll_4, text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_4)); + + std::wstring latest_version(L"1.0.4.0"); + installer::RemoveOldVersionDirs(chrome_dir, latest_version); + + // old versions should be gone + EXPECT_FALSE(file_util::PathExists(chrome_dir_1)); + EXPECT_FALSE(file_util::PathExists(chrome_dir_2)); + EXPECT_FALSE(file_util::PathExists(chrome_dir_3)); + // the latest version should stay + EXPECT_TRUE(file_util::PathExists(chrome_dll_4)); +} + +// Delete older version directories, keeping the one in used intact. +TEST_F(SetupHelperTest, DeleteInUsed) { + // Create a Chrome dir + std::wstring chrome_dir(test_dir_); + file_util::AppendToPath(&chrome_dir, L"chrome"); + CreateDirectory(chrome_dir.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir)); + + std::wstring chrome_dir_1(chrome_dir); + file_util::AppendToPath(&chrome_dir_1, L"1.0.1.0"); + CreateDirectory(chrome_dir_1.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir_1)); + + std::wstring chrome_dir_2(chrome_dir); + file_util::AppendToPath(&chrome_dir_2, L"1.0.2.0"); + CreateDirectory(chrome_dir_2.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir_2)); + + std::wstring chrome_dir_3(chrome_dir); + file_util::AppendToPath(&chrome_dir_3, L"1.0.3.0"); + CreateDirectory(chrome_dir_3.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir_3)); + + std::wstring chrome_dir_4(chrome_dir); + file_util::AppendToPath(&chrome_dir_4, L"1.0.4.0"); + CreateDirectory(chrome_dir_4.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(chrome_dir_4)); + + std::wstring chrome_dll_1(chrome_dir_1); + file_util::AppendToPath(&chrome_dll_1, L"chrome.dll"); + CreateTextFile(chrome_dll_1, text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_1)); + + std::wstring chrome_dll_2(chrome_dir_2); + file_util::AppendToPath(&chrome_dll_2, L"chrome.dll"); + CreateTextFile(chrome_dll_2, text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_2)); + + // Open the file to make it in use. + std::ofstream file; + file.open(chrome_dll_2.c_str()); + + std::wstring chrome_othera_2(chrome_dir_2); + file_util::AppendToPath(&chrome_othera_2, L"othera.dll"); + CreateTextFile(chrome_othera_2, text_content_2); + ASSERT_TRUE(file_util::PathExists(chrome_othera_2)); + + std::wstring chrome_otherb_2(chrome_dir_2); + file_util::AppendToPath(&chrome_otherb_2, L"otherb.dll"); + CreateTextFile(chrome_otherb_2, text_content_2); + ASSERT_TRUE(file_util::PathExists(chrome_otherb_2)); + + std::wstring chrome_dll_3(chrome_dir_3); + file_util::AppendToPath(&chrome_dll_3, L"chrome.dll"); + CreateTextFile(chrome_dll_3, text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_3)); + + std::wstring chrome_dll_4(chrome_dir_4); + file_util::AppendToPath(&chrome_dll_4, L"chrome.dll"); + CreateTextFile(chrome_dll_4, text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_4)); + + std::wstring latest_version(L"1.0.4.0"); + installer::RemoveOldVersionDirs(chrome_dir, latest_version); + + // old versions not in used should be gone + EXPECT_FALSE(file_util::PathExists(chrome_dir_1)); + EXPECT_FALSE(file_util::PathExists(chrome_dir_3)); + // every thing under in used version should stay + EXPECT_TRUE(file_util::PathExists(chrome_dir_2)); + EXPECT_TRUE(file_util::PathExists(chrome_dll_2)); + EXPECT_TRUE(file_util::PathExists(chrome_othera_2)); + EXPECT_TRUE(file_util::PathExists(chrome_otherb_2)); + // the latest version should stay + EXPECT_TRUE(file_util::PathExists(chrome_dll_4)); +} diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc new file mode 100644 index 0000000..6424bd6 --- /dev/null +++ b/chrome/installer/util/install_util.cc @@ -0,0 +1,120 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// See the corresponding header file for description of the functions in this +// file. + +#include "install_util.h" + +#include <windows.h> + +#include "base/logging.h" +#include "base/registry.h" +#include "base/string_util.h" +#include "chrome/installer/util/google_update_constants.h" + + +std::wstring InstallUtil::FormatLastWin32Error() { + unsigned messageid = GetLastError(); + wchar_t* string_buffer = NULL; + unsigned string_length = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, messageid, 0, reinterpret_cast<wchar_t *>(&string_buffer), 0, NULL); + + std::wstring formatted_string; + if (string_buffer) { + formatted_string = string_buffer; + LocalFree(reinterpret_cast<HLOCAL>(string_buffer)); + } else { + // The formating failed. simply convert the message value into a string. + SStringPrintf(&formatted_string, L"message number %d", messageid); + } + return formatted_string; +} + + +std::wstring InstallUtil::GetChromeGoogleUpdateKey() { + std::wstring chrome_google_update_key(google_update::kRegPathClients); + chrome_google_update_key.append(L"\\"); + chrome_google_update_key.append(google_update::kChromeGuid); + return chrome_google_update_key; +} + +installer::Version* InstallUtil::GetChromeVersion(bool system_install) { + RegKey key; + std::wstring version_str; + + HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + if (!key.Open(reg_root, GetChromeGoogleUpdateKey().c_str(), KEY_READ) || + !key.ReadValue(google_update::kRegVersionField, &version_str)) { + LOG(INFO) << "No existing Chrome install found."; + key.Close(); + return NULL; + } + key.Close(); + LOG(INFO) << "Existing Chrome version found " << version_str; + return installer::Version::GetVersionFromString(version_str); +} + +std::wstring InstallUtil::GetNewGoogleUpdateApKey(bool diff_install, + installer_util::InstallStatus status, const std::wstring& value) { + // Magic suffix that we need to add or remove to "ap" key value. + const std::wstring kMagicSuffix = L"-full"; + + bool has_magic_string = false; + if ((value.length() >= kMagicSuffix.length()) && + (value.rfind(kMagicSuffix) == (value.length() - kMagicSuffix.length()))) { + LOG(INFO) << "Incremental installer failure key already set."; + has_magic_string = true; + } + + std::wstring new_value(value); + if ((!diff_install || InstallSuccessful(status)) && has_magic_string) { + LOG(INFO) << "Removing failure key from value " << value; + new_value = value.substr(0, value.length() - kMagicSuffix.length()); + } else if ((diff_install && !InstallSuccessful(status)) && + !has_magic_string) { + LOG(INFO) << "Incremental installer failed, setting failure key."; + new_value.append(kMagicSuffix); + } + + return new_value; +} + +bool InstallUtil::InstallSuccessful(installer_util::InstallStatus status) { + switch (status) { + case installer_util::FIRST_INSTALL_SUCCESS: + case installer_util::INSTALL_REPAIRED: + case installer_util::NEW_VERSION_UPDATED: + case installer_util::HIGHER_VERSION_EXISTS: + return true; + default: + return false; + } +} diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h new file mode 100644 index 0000000..8ef79bf --- /dev/null +++ b/chrome/installer/util/install_util.h @@ -0,0 +1,89 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file declares utility functions for the installer. The original reason +// for putting these functions in installer\util library is so that we can +// separate out the critical logic and write unit tests for it. + +#ifndef CHROME_INSTALLER_UTIL_INSTALL_UTIL_H__ +#define CHROME_INSTALLER_UTIL_INSTALL_UTIL_H__ + +#include <string> + +#include "base/basictypes.h" +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/version.h" + +// This is a utility class that provides common installation related +// utility methods that can be used by installer and also unit tested +// independently. +class InstallUtil { + public: + // Gets the last Win32 error and generates a human readable message string. + // Uses the Win32 API GetLastError() to get the last error and API + // FormatMessage() to generate a string. This function has been copied + // from chrome\common\win_util.{h.cc} to avoid making setup.exe dependent + // on all the other libs (base_gfx, libjpeg, libpng and others) that we + // need to pull in and the size of setup.exe goes up by ~140KB. + static std::wstring FormatLastWin32Error(); + + // This method gets the Google Update registry key path for Chrome. + // i.e. - Software\Google\Update\Clients\<chrome-guid>"; + static std::wstring GetChromeGoogleUpdateKey(); + + // Find the version of Chrome installed on the system by checking the + // Google Update registry key. Returns the version or NULL if no version is + // found. + // system_install: if true, looks for version number under the HKLM root, + // otherwise looks under the HKCU. + static installer::Version * GetChromeVersion(bool system_install); + + // This method generates the new value for Oamaha "ap" key for Chrome + // based on whether we are doing incremental install (or not) and whether + // the install succeeded. + // - If install worked, remove the magic string (if present). + // - If incremental installer failed, append a magic string (if + // not present already). + // - If full installer failed, still remove this magic + // string (if it is present already). + // + // diff_install: tells whether this is incremental install or not. + // install_status: if 0, means installation was successful. + // value: current value of Google Update "ap" key. + static std::wstring GetNewGoogleUpdateApKey(bool diff_install, + installer_util::InstallStatus status, const std::wstring& value); + + // Given an InstallStatus it tells whether the install was sucessful or not. + static bool InstallSuccessful(installer_util::InstallStatus status); + private: + DISALLOW_EVIL_CONSTRUCTORS(InstallUtil); +}; + + +#endif // CHROME_INSTALLER_UTIL_INSTALL_UTIL_H__ diff --git a/chrome/installer/util/install_util_unittest.cc b/chrome/installer/util/install_util_unittest.cc new file mode 100644 index 0000000..b0a816a --- /dev/null +++ b/chrome/installer/util/install_util_unittest.cc @@ -0,0 +1,97 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Unit tests for InstallUtil class. + +#include <windows.h> + +#include "chrome/installer/util/install_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +class InstallUtilTest : public testing::Test { + protected: + virtual void SetUp() { + // Currently no setup required. + } + + virtual void TearDown() { + // Currently no tear down required. + } +}; +} // namespace + +TEST_F(InstallUtilTest, GetNewGoogleUpdateApKeyTest) { + installer_util::InstallStatus s = installer_util::FIRST_INSTALL_SUCCESS; + installer_util::InstallStatus f = installer_util::INSTALL_FAILED; + + // Incremental Installer that worked. + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L""), L""); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"1.1"), L"1.1"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"1.1-dev"), + L"1.1-dev"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"-full"), L""); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"1.1-full"), + L"1.1"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"1.1-dev-full"), + L"1.1-dev"); + + // Incremental Installer that failed. + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L""), L"-full"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"1.1"), L"1.1-full"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"1.1-dev"), + L"1.1-dev-full"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"-full"), L"-full"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"1.1-full"), + L"1.1-full"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"1.1-dev-full"), + L"1.1-dev-full"); + + // Full Installer that worked. + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L""), L""); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"1.1"), L"1.1"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"1.1-dev"), + L"1.1-dev"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"-full"), L""); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"1.1-full"), + L"1.1"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"1.1-dev-full"), + L"1.1-dev"); + + // Full Installer that failed. + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L""), L""); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"1.1"), L"1.1"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"1.1-dev"), + L"1.1-dev"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"-full"), L""); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"1.1-full"), + L"1.1"); + EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"1.1-dev-full"), + L"1.1-dev"); +} diff --git a/chrome/installer/util/installer_unittests.vcproj b/chrome/installer/util/installer_unittests.vcproj new file mode 100644 index 0000000..b7e3eef --- /dev/null +++ b/chrome/installer/util/installer_unittests.vcproj @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="installer_unittests" + ProjectGUID="{903F8C1E-537A-4C9E-97BE-075147CBE769}" + RootNamespace="installer_unittests" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="1" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)\tools\build\win\unit_test.vsprops;$(SolutionDir)\installer\util\using_util.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + AdditionalManifestFiles="$(SolutionDir)installer\mini_installer\mini_installer.exe.manifest" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="1" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)\tools\build\win\unit_test.vsprops;$(SolutionDir)\installer\util\using_util.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + AdditionalManifestFiles="$(SolutionDir)installer\mini_installer\mini_installer.exe.manifest" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="support" + > + <File + RelativePath="run_all_unittests.cc" + > + </File> + </Filter> + <Filter + Name="tests" + > + <File + RelativePath="copy_tree_work_item_unittest.cc" + > + </File> + <File + RelativePath="create_dir_work_item_unittest.cc" + > + </File> + <File + RelativePath="create_reg_key_work_item_unittest.cc" + > + </File> + <File + RelativePath="delete_tree_work_item_unittest.cc" + > + </File> + <File + RelativePath="helper_unittest.cc" + > + </File> + <File + RelativePath="install_util_unittest.cc" + > + </File> + <File + RelativePath="set_reg_value_work_item_unittest.cc" + > + </File> + <File + RelativePath="..\setup\setup_constants.cc" + > + </File> + <File + RelativePath="work_item_list_unittest.cc" + > + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/installer/util/l10n_string_util.cc b/chrome/installer/util/l10n_string_util.cc new file mode 100644 index 0000000..b10e1a4 --- /dev/null +++ b/chrome/installer/util/l10n_string_util.cc @@ -0,0 +1,148 @@ +#include <atlbase.h> + +#include <map> + +#include "base/logging.h" +#include "base/string_util.h" + +#include "setup_strings.h" + +namespace { + +// Gets the language from the OS. If we're unable to get the system language, +// defaults to en-us. +std::wstring GetSystemLanguage() { + static std::wstring language; + if (!language.empty()) + return language; + // We don't have ICU at this point, so we use win32 apis. + LCID id = GetThreadLocale(); + int length = GetLocaleInfo(id, LOCALE_SISO639LANGNAME, 0, 0); + if (0 == length) + return false; + length = GetLocaleInfo(id, LOCALE_SISO639LANGNAME, + WriteInto(&language, length), length); + DCHECK(length == language.length() + 1); + StringToLowerASCII(&language); + + // Add the country if we need it. + std::wstring country; + length = GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, 0, 0); + if (0 != length) { + length = GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, + WriteInto(&country, length), length); + DCHECK(length == country.length() + 1); + StringToLowerASCII(&country); + if (L"en" == language) { + if (L"gb" == country) { + language.append(L"-gb"); + } else { + language.append(L"-us"); + } + } else if (L"es" == language && L"es" != country) { + language.append(L"-419"); + } else if (L"pt" == language) { + if (L"br" == country) { + language.append(L"-br"); + } else { + language.append(L"-pt"); + } + } else if (L"zh" == language) { + if (L"tw" == country) { + language.append(L"-tw"); + } else { + language.append(L"-cn"); + } + } + } + + if (language.empty()) + language = L"en-us"; + + return language; +} + +// This method returns the appropriate language offset given the language as a +// string. Note: This method is not thread safe because of how we create +// |offset_map|. +int GetLanguageOffset(const std::wstring& language) { + static std::map<std::wstring, int> offset_map; + if (offset_map.empty()) { + offset_map[L"ar"] = IDS_L10N_OFFSET_AR; + offset_map[L"bg"] = IDS_L10N_OFFSET_BG; + offset_map[L"ca"] = IDS_L10N_OFFSET_CA; + offset_map[L"cs"] = IDS_L10N_OFFSET_CS; + offset_map[L"da"] = IDS_L10N_OFFSET_DA; + offset_map[L"de"] = IDS_L10N_OFFSET_DE; + offset_map[L"el"] = IDS_L10N_OFFSET_EL; + offset_map[L"en-gb"] = IDS_L10N_OFFSET_EN_GB; + offset_map[L"en-us"] = IDS_L10N_OFFSET_EN_US; + offset_map[L"es"] = IDS_L10N_OFFSET_ES; + offset_map[L"es-419"] = IDS_L10N_OFFSET_ES_419; + offset_map[L"et"] = IDS_L10N_OFFSET_ET; + offset_map[L"fi"] = IDS_L10N_OFFSET_FI; + offset_map[L"fil"] = IDS_L10N_OFFSET_FIL; + offset_map[L"fr"] = IDS_L10N_OFFSET_FR; + offset_map[L"he"] = IDS_L10N_OFFSET_HE; + offset_map[L"hi"] = IDS_L10N_OFFSET_HI; + offset_map[L"hr"] = IDS_L10N_OFFSET_HR; + offset_map[L"hu"] = IDS_L10N_OFFSET_HU; + offset_map[L"id"] = IDS_L10N_OFFSET_ID; + offset_map[L"it"] = IDS_L10N_OFFSET_IT; + offset_map[L"ja"] = IDS_L10N_OFFSET_JA; + offset_map[L"ko"] = IDS_L10N_OFFSET_KO; + offset_map[L"lt"] = IDS_L10N_OFFSET_LT; + offset_map[L"lv"] = IDS_L10N_OFFSET_LV; + // Google web properties use no for nb. Handle both just to be safe. + offset_map[L"nb"] = IDS_L10N_OFFSET_NO; + offset_map[L"nl"] = IDS_L10N_OFFSET_NL; + offset_map[L"no"] = IDS_L10N_OFFSET_NO; + offset_map[L"pl"] = IDS_L10N_OFFSET_PL; + offset_map[L"pt-br"] = IDS_L10N_OFFSET_PT_BR; + offset_map[L"pt-pt"] = IDS_L10N_OFFSET_PT_PT; + offset_map[L"ro"] = IDS_L10N_OFFSET_RO; + offset_map[L"ru"] = IDS_L10N_OFFSET_RU; + offset_map[L"sk"] = IDS_L10N_OFFSET_SK; + offset_map[L"sl"] = IDS_L10N_OFFSET_SL; + offset_map[L"sr"] = IDS_L10N_OFFSET_SR; + offset_map[L"sv"] = IDS_L10N_OFFSET_SV; + offset_map[L"th"] = IDS_L10N_OFFSET_TH; + offset_map[L"tr"] = IDS_L10N_OFFSET_TR; + offset_map[L"uk"] = IDS_L10N_OFFSET_UK; + offset_map[L"vi"] = IDS_L10N_OFFSET_VI; + offset_map[L"zh-cn"] = IDS_L10N_OFFSET_ZH_CN; + offset_map[L"zh-tw"] = IDS_L10N_OFFSET_ZH_TW; + } + + std::map<std::wstring, int>::iterator it = offset_map.find( + StringToLowerASCII(language)); + if (it != offset_map.end()) + return it->second; + + NOTREACHED() << "unknown system language-country"; + + // Fallback on the en-US offset just in case. + return IDS_L10N_OFFSET_EN_US; +} + +} // namespace + +namespace installer_util { + +std::wstring GetLocalizedString(int base_message_id) { + std::wstring language = GetSystemLanguage(); + std::wstring localized_string; + + int message_id = base_message_id + GetLanguageOffset(language); + const ATLSTRINGRESOURCEIMAGE* image = AtlGetStringResourceImage( + _AtlBaseModule.GetModuleInstance(), message_id); + if (image) { + localized_string = std::wstring(image->achString, image->nLength); + } else { + NOTREACHED() << "Unable to find resource id " << message_id; + } + + return localized_string; +} + +} // namespace installer_util diff --git a/chrome/installer/util/l10n_string_util.h b/chrome/installer/util/l10n_string_util.h new file mode 100644 index 0000000..91989ce --- /dev/null +++ b/chrome/installer/util/l10n_string_util.h @@ -0,0 +1,52 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file contains helper functions for getting strings that are included in +// our DLL for all languages (i.e., does not come from our language DLL). +// +// These resource strings are organized such that we can get a localized string +// by taking the base resource ID and adding a language offset. For example, +// to get the resource id for the localized product name in en-US, we take +// IDS_PRODUCT_NAME_BASE + IDS_L10N_OFFSET_EN_US. + +#ifndef CHROME_INSTALLER_UTIL_L10N_STRING_UTIL_H_ +#define CHROME_INSTALLER_UTIL_L10N_STRING_UTIL_H_ + +#include <string> + +namespace installer_util { + +// Given a string base id, return the localized version of the string based on +// the system language. This is used for shortcuts placed on the user's +// desktop. +std::wstring GetLocalizedString(int base_message_id); + +} + +#endif // CHROME_INSTALLER_UTIL_L10N_STRING_UTIL_H_ diff --git a/chrome/installer/util/logging_installer.cc b/chrome/installer/util/logging_installer.cc new file mode 100644 index 0000000..ab0786d --- /dev/null +++ b/chrome/installer/util/logging_installer.cc @@ -0,0 +1,92 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include "chrome/installer/util/logging_installer.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "chrome/installer/util/util_constants.h" + +namespace installer { + +// This should be true for the period between the end of +// InitInstallerLogging() and the beginning of EndInstallerLogging(). +bool installer_logging_ = false; + +void InitInstallerLogging(const CommandLine& command_line) { + + if (installer_logging_) + return; + + if (command_line.HasSwitch(installer_util::switches::kDisableLogging)) { + installer_logging_ = true; + return; + } + + logging::InitLogging(GetLogFilePath(command_line).c_str(), + logging::LOG_ONLY_TO_FILE, + logging::LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE); + + if (command_line.HasSwitch(installer_util::switches::kVerboseLogging)) { + logging::SetMinLogLevel(logging::LOG_INFO); + } else { + logging::SetMinLogLevel(logging::LOG_ERROR); + } + + installer_logging_ = true; +} + +void EndInstallerLogging() { + logging::CloseLogFile(); + + installer_logging_ = false; +} + +std::wstring GetLogFilePath(const CommandLine& command_line) { + if (command_line.HasSwitch(installer_util::switches::kLogFile)) { + return command_line.GetSwitchValue(installer_util::switches::kLogFile); + } + + const std::wstring log_filename(L"chrome_installer.log"); + std::wstring log_path; + + if (PathService::Get(base::DIR_TEMP, &log_path)) { + file_util::AppendToPath(&log_path, log_filename); + return log_path; + } else { + return log_filename; + } +} + +} // namespace installer diff --git a/chrome/installer/util/logging_installer.h b/chrome/installer/util/logging_installer.h new file mode 100644 index 0000000..769dec3 --- /dev/null +++ b/chrome/installer/util/logging_installer.h @@ -0,0 +1,52 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_INSTALLER_LOGGING_INSTALLER_H__ +#define CHROME_INSTALLER_LOGGING_INSTALLER_H__ + +#include <string> + +#include "base/logging.h" + +class CommandLine; + +namespace installer { + +// Call to initialize logging for Chrome installer. +void InitInstallerLogging(const CommandLine& command_line); + +// Call when done using logging for Chrome installer. +void EndInstallerLogging(); + +// Returns the full path of the log file. +std::wstring GetLogFilePath(const CommandLine& command_line); + +} // namespace installer + +#endif diff --git a/chrome/installer/util/lzma_util.cc b/chrome/installer/util/lzma_util.cc new file mode 100644 index 0000000..45c30c4 --- /dev/null +++ b/chrome/installer/util/lzma_util.cc @@ -0,0 +1,210 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "lzma_util.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_util.h" + +extern "C" { +#include "Archive/7z/7zExtract.h" +#include "Archive/7z/7zIn.h" +#include "7zCrc.h" +} + + +namespace installer { + +typedef struct _CFileInStream { + ISzInStream InStream; + HANDLE File; +} CFileInStream; + + +size_t LzmaReadFile(HANDLE file, void *data, size_t size) { + if (size == 0) + return 0; + + size_t processedSize = 0; + do { + DWORD processedLoc = 0; + BOOL res = ReadFile(file, data, (DWORD) size, &processedLoc, NULL); + data = (void *)((unsigned char *) data + processedLoc); + size -= processedLoc; + processedSize += processedLoc; + if (!res || processedLoc == 0) + break; + } while (size > 0); + + return processedSize; +} + +SZ_RESULT SzFileSeekImp(void *object, CFileSize pos) { + CFileInStream *s = (CFileInStream *) object; + LARGE_INTEGER value; + value.LowPart = (DWORD) pos; + value.HighPart = (LONG) ((UInt64) pos >> 32); + value.LowPart = SetFilePointer(s->File, value.LowPart, &value.HighPart, + FILE_BEGIN); + if (value.LowPart == 0xFFFFFFFF) { + if(GetLastError() != NO_ERROR) { + return SZE_FAIL; + } + } + return SZ_OK; +} + +SZ_RESULT SzFileReadImp(void *object, void **buffer, + size_t maxRequiredSize, size_t *processedSize) { + const int kBufferSize = 1 << 12; + static Byte g_Buffer[kBufferSize]; + if (maxRequiredSize > kBufferSize) { + maxRequiredSize = kBufferSize; + } + + CFileInStream *s = (CFileInStream *) object; + size_t processedSizeLoc; + processedSizeLoc = LzmaReadFile(s->File, g_Buffer, maxRequiredSize); + *buffer = g_Buffer; + if (processedSize != 0) { + *processedSize = processedSizeLoc; + } + return SZ_OK; +} + + +LzmaUtil::LzmaUtil() : archive_handle_(NULL) {} + +LzmaUtil::~LzmaUtil() { + if (archive_handle_) CloseArchive(); +} + +DWORD LzmaUtil::OpenArchive(const std::wstring& archivePath) { + DWORD ret = NO_ERROR; + archive_handle_ = CreateFile(archivePath.c_str(), GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (archive_handle_ == INVALID_HANDLE_VALUE) { + ret = GetLastError(); + } + return ret; +} + +// Unpacks the archive to the given location. +DWORD LzmaUtil::UnPack(const std::wstring& location) { + CFileInStream archiveStream; + ISzAlloc allocImp; + ISzAlloc allocTempImp; + CArchiveDatabaseEx db; + DWORD ret = NO_ERROR; + + archiveStream.File = archive_handle_; + archiveStream.InStream.Read = SzFileReadImp; + archiveStream.InStream.Seek = SzFileSeekImp; + allocImp.Alloc = SzAlloc; + allocImp.Free = SzFree; + allocTempImp.Alloc = SzAllocTemp; + allocTempImp.Free = SzFreeTemp; + + CrcGenerateTable(); + SzArDbExInit(&db); + if ((ret = SzArchiveOpen(&archiveStream.InStream, &db, + &allocImp, &allocTempImp)) != SZ_OK) { + return ret; + } + + Byte *outBuffer = 0; // it must be 0 before first call for each new archive + UInt32 blockIndex = 0xFFFFFFFF; // can have any value if outBuffer = 0 + size_t outBufferSize = 0; // can have any value if outBuffer = 0 + for (unsigned int i = 0; i < db.Database.NumFiles; i++) { + DWORD written; + size_t offset; + size_t outSizeProcessed; + CFileItem *f = db.Database.Files + i; + + if ((ret = SzExtract(&archiveStream.InStream, &db, i, &blockIndex, + &outBuffer, &outBufferSize, &offset, &outSizeProcessed, + &allocImp, &allocTempImp)) != SZ_OK) { + break; + } + + // Append location to the file path in archive, to get full path. + std::wstring wfileName(location); + file_util::AppendToPath(&wfileName, UTF8ToWide(f->Name)); + + // If archive entry is directory create it and move on to the next entry. + if (f->IsDirectory) { + file_util::CreateDirectory(wfileName); + continue; + } + + HANDLE hFile; + std::wstring directory = file_util::GetDirectoryFromPath(wfileName); + file_util::CreateDirectory(directory); + + hFile = CreateFile(wfileName.c_str(), GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + ret = GetLastError(); + break; + } + + WriteFile(hFile, outBuffer + offset, (DWORD) outSizeProcessed, + &written, NULL); + if (written != outSizeProcessed) { + ret = GetLastError(); + CloseHandle(hFile); + break; + } + + if (f->IsLastWriteTimeDefined) { + if (!SetFileTime(hFile, NULL, NULL, + (const FILETIME *)&(f->LastWriteTime))) { + ret = GetLastError(); + CloseHandle(hFile); + break; + } + } + if (!CloseHandle(hFile)) { + ret = GetLastError(); + break; + } + } // for loop + + allocImp.Free(outBuffer); + SzArDbExFree(&db, allocImp.Free); + return ret; +} + +void LzmaUtil::CloseArchive() { + CloseHandle(archive_handle_); + archive_handle_ = NULL; +} + +} // namespace installer diff --git a/chrome/installer/util/lzma_util.h b/chrome/installer/util/lzma_util.h new file mode 100644 index 0000000..67b98d2 --- /dev/null +++ b/chrome/installer/util/lzma_util.h @@ -0,0 +1,62 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_INSTALLER_UTIL_LZMA_UTIL_H__ +#define CHROME_INSTALLER_UTIL_LZMA_UTIL_H__ + +#include <string> +#include <windows.h> + +#include "base/basictypes.h" + +namespace installer { + +// This is a utility class that acts as a wrapper around LZMA SDK library +class LzmaUtil { + public: + LzmaUtil(); + ~LzmaUtil(); + + DWORD OpenArchive(const std::wstring& archivePath); + + // Unpacks the archive to the given location + DWORD UnPack(const std::wstring& location); + + void CloseArchive(); + + private: + HANDLE archive_handle_; + + DISALLOW_EVIL_CONSTRUCTORS(LzmaUtil); +}; + +} // namespace installer + + +#endif // CHROME_INSTALLER_UTIL_LZMA_UTIL_H__ diff --git a/chrome/installer/util/prebuild/create_string_rc.bat b/chrome/installer/util/prebuild/create_string_rc.bat new file mode 100644 index 0000000..51d1c39 --- /dev/null +++ b/chrome/installer/util/prebuild/create_string_rc.bat @@ -0,0 +1,10 @@ +:: A wrapper file for running create_string_rc.py from visual studio. + +setlocal +set OUTFILE=%~1 +set PYTHON=%~dp0..\..\..\..\third_party\python_24\python.exe + +:: Add grit to the python path so we can import FP.py. +set PYTHONPATH=%~dp0..\..\..\..\tools\grit\grit\extern + +%PYTHON% create_string_rc.py %OUTFILE% diff --git a/chrome/installer/util/prebuild/create_string_rc.py b/chrome/installer/util/prebuild/create_string_rc.py new file mode 100644 index 0000000..a617646 --- /dev/null +++ b/chrome/installer/util/prebuild/create_string_rc.py @@ -0,0 +1,180 @@ +#!/usr/bin/python +# Copyright 2008 Google Inc. All rights reserved. + +"""This script generates an rc file and header (setup_strings.{rc,h}) to be +included in setup.exe. The rc file includes translations for strings pulled +from generated_resource.grd and the localized .xtb files. + +The header file includes IDs for each string, but also has values to allow +getting a string based on a language offset. For example, the header file +looks like this: + +#define IDS_L10N_OFFSET_AR 0 +#define IDS_L10N_OFFSET_BG 1 +#define IDS_L10N_OFFSET_CA 2 +... +#define IDS_L10N_OFFSET_ZH_TW 41 + +#define IDS_MY_STRING_AR 1600 +#define IDS_MY_STRING_BG 1601 +... +#define IDS_MY_STRING_BASE IDS_MY_STRING_AR + +This allows us to lookup an an ID for a string by adding IDS_MY_STRING_BASE and +IDS_L10N_OFFSET_* for the language we are interested in. +""" + +import glob +import os +import sys +from xml.dom import minidom + +from google import path_utils + +import FP + +# The IDs of strings we want to import from generated_resources.grd and include +# in setup.exe's resources. +kStringIds = [ + 'IDS_PRODUCT_NAME', + 'IDS_UNINSTALL_CHROME', +] + +# The ID of the first resource string. +kFirstResourceID = 1600 + +class TranslationStruct: + """A helper struct that holds information about a single translation.""" + def __init__(self, resource_id_str, language, translation): + self.resource_id_str = resource_id_str + self.language = language + self.translation = translation + + def __cmp__(self, other): + """Allow TranslationStructs to be sorted by id.""" + return cmp(self.resource_id_str, other.resource_id_str) + + +def CollectTranslatedStrings(): + """Collects all the translations for "Google Chrome" from the XTB files. + Returns a list of tuples of (language, translated string). The list is + sorted by language codes.""" + kGeneratedResourcesPath = os.path.join(path_utils.ScriptDir(), '..', '..', + '..', 'app/generated_resources.grd') + kTranslationDirectory = os.path.join(path_utils.ScriptDir(), '..', '..', + '..', 'app', 'resources') + kTranslationFiles = glob.glob(os.path.join(kTranslationDirectory, '*.xtb')) + + # Get the strings out of generated_resources.grd. + dom = minidom.parse(kGeneratedResourcesPath) + # message_nodes is a list of message dom nodes corresponding to the string + # ids we care about. We want to make sure that this list is in the same + # order as kStringIds so we can associate them together. + message_nodes = [] + all_message_nodes = dom.getElementsByTagName('message') + for string_id in kStringIds: + message_nodes.append([x for x in all_message_nodes if + x.getAttribute('name') == string_id][0]) + message_texts = [node.firstChild.nodeValue.strip() for node in message_nodes] + + # The fingerprint of the string is the message ID in the translation files + # (xtb files). + translation_ids = [str(FP.FingerPrint(text)) for text in message_texts] + + # Manually put _EN_US in the list of translated strings because it doesn't + # have a .xtb file. + translated_strings = [] + for string_id, message_text in zip(kStringIds, message_texts): + translated_strings.append(TranslationStruct(string_id + '_EN_US', + 'EN_US', + message_text)) + + # Gather the translated strings from the .xtb files. If an .xtb file doesn't + # have the string we want, use the en-US string. + for xtb_filename in kTranslationFiles: + dom = minidom.parse(xtb_filename) + language = dom.documentElement.getAttribute('lang') + language = language.replace('-', '_').upper() + translation_nodes = {} + for translation_node in dom.getElementsByTagName('translation'): + translation_id = translation_node.getAttribute('id') + if translation_id in translation_ids: + translation_nodes[translation_id] = (translation_node.firstChild + .nodeValue + .strip()) + for i, string_id in enumerate(kStringIds): + translated_string = translation_nodes.get(translation_ids[i], + message_texts[i]) + translated_strings.append(TranslationStruct(string_id + '_' + language, + language, + translated_string)) + + translated_strings.sort() + return translated_strings + +def WriteRCFile(translated_strings, out_filename): + """Writes a resource (rc) file with all the language strings provided in + |translated_strings|.""" + kHeaderText = ( + u'#include "setup_strings.h"\n\n' + u'STRINGTABLE\n' + u'BEGIN\n' + ) + kFooterText = ( + u'END\n' + ) + lines = [kHeaderText] + for translation_struct in translated_strings: + lines.append(u' %s "%s"\n' % (translation_struct.resource_id_str, + translation_struct.translation)) + lines.append(kFooterText) + outfile = open(out_filename, 'wb') + outfile.write(''.join(lines).encode('utf-16')) + outfile.close() + +def WriteHeaderFile(translated_strings, out_filename): + """Writes a .h file with resource ids. This file can be included by the + executable to refer to identifiers.""" + lines = [] + + # Write the values for how the languages ids are offset. + seen_languages = set() + offset_id = 0 + for translation_struct in translated_strings: + lang = translation_struct.language + if lang not in seen_languages: + seen_languages.add(lang) + lines.append(u'#define IDS_L10N_OFFSET_%s %s' % (lang, offset_id)) + offset_id += 1 + else: + break + + # Write the resource ids themselves. + resource_id = kFirstResourceID + for translation_struct in translated_strings: + lines.append(u'#define %s %s' % (translation_struct.resource_id_str, + resource_id)) + resource_id += 1 + + # Write out base ID values. + for string_id in kStringIds: + lines.append(u'#define %s_BASE %s_%s' % (string_id, + string_id, + translated_strings[0].language)) + + outfile = open(out_filename, 'wb') + outfile.write('\n'.join(lines)) + outfile.write('\n') # .rc files must end in a new line + outfile.close() + +def main(argv): + translated_strings = CollectTranslatedStrings() + kFilebase = os.path.join(argv[1], 'setup_strings') + WriteRCFile(translated_strings, kFilebase + '.rc') + WriteHeaderFile(translated_strings, kFilebase + '.h') + +if '__main__' == __name__: + if len(sys.argv) < 2: + print 'Usage:\n %s <output_directory>' % sys.argv[0] + sys.exit(1) + main(sys.argv) diff --git a/chrome/installer/util/prebuild/util_prebuild.vcproj b/chrome/installer/util/prebuild/util_prebuild.vcproj new file mode 100644 index 0000000..1269dde --- /dev/null +++ b/chrome/installer/util/prebuild/util_prebuild.vcproj @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="util_prebuild" + ProjectGUID="{0026A376-C4F1-4575-A1BA-578C69F07013}" + RootNamespace="util_prebuild" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="1" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + CommandLine="create_string_rc.bat $(IntDir)" + AdditionalDependencies="create_string_rc.py;$(SolutionDir)\app\generated_resources.grd" + Outputs="$(IntDir)\setup_strings.rc;$(IntDir)\setup_strings.h" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="1" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + CommandLine="create_string_rc.bat $(IntDir)" + AdditionalDependencies="create_string_rc.py;$(SolutionDir)\app\generated_resources.grd" + Outputs="$(IntDir)\setup_strings.rc;$(IntDir)\setup_strings.h" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/installer/util/prebuild/util_prebuild.vsprops b/chrome/installer/util/prebuild/util_prebuild.vsprops new file mode 100644 index 0000000..2fa7bc5 --- /dev/null +++ b/chrome/installer/util/prebuild/util_prebuild.vsprops @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="util_prebuild" + > + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="$(IntDir)\..\util_prebuild\" + /> +</VisualStudioPropertySheet> diff --git a/chrome/installer/util/run_all_unittests.cc b/chrome/installer/util/run_all_unittests.cc new file mode 100644 index 0000000..69b2d3f --- /dev/null +++ b/chrome/installer/util/run_all_unittests.cc @@ -0,0 +1,34 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/test_suite.h" + +int main(int argc, char** argv) { + return TestSuite(argc, argv).Run(); +} diff --git a/chrome/installer/util/set_reg_value_work_item.cc b/chrome/installer/util/set_reg_value_work_item.cc new file mode 100644 index 0000000..8f8d4b4 --- /dev/null +++ b/chrome/installer/util/set_reg_value_work_item.cc @@ -0,0 +1,176 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/registry.h" +#include "chrome/installer/util/set_reg_value_work_item.h" +#include "chrome/installer/util/logging_installer.h" + +SetRegValueWorkItem::~SetRegValueWorkItem() { +} + +SetRegValueWorkItem::SetRegValueWorkItem(HKEY predefined_root, + std::wstring key_path, + std::wstring value_name, + std::wstring value_data, + bool overwrite) + : predefined_root_(predefined_root), + key_path_(key_path), + value_name_(value_name), + value_data_str_(value_data), + overwrite_(overwrite), + status_(SET_VALUE), + is_str_type_(true) { +} + +SetRegValueWorkItem::SetRegValueWorkItem(HKEY predefined_root, + std::wstring key_path, + std::wstring value_name, + DWORD value_data, + bool overwrite) + : predefined_root_(predefined_root), + key_path_(key_path), + value_name_(value_name), + value_data_dword_(value_data), + overwrite_(overwrite), + status_(SET_VALUE), + is_str_type_(false) { +} + +bool SetRegValueWorkItem::Do() { + if (status_ != SET_VALUE) { + // we already did something. + LOG(ERROR) << "multiple calls to Do()"; + return false; + } + + RegKey key; + if (!key.Open(predefined_root_, key_path_.c_str(), + KEY_READ | KEY_SET_VALUE)) { + LOG(ERROR) << "can not open " << key_path_; + status_ = VALUE_UNCHANGED; + return false; + } + + bool result = false; + if (key.ValueExists(value_name_.c_str())) { + if (overwrite_) { + bool success = true; + // Read previous value for rollback and write new value + if (is_str_type_) { + std::wstring data; + if (key.ReadValue(value_name_.c_str(), &data)) { + previous_value_str_.assign(data); + } + success = key.WriteValue(value_name_.c_str(), value_data_str_.c_str()); + } else { + DWORD data; + if (key.ReadValueDW(value_name_.c_str(), &data)) { + previous_value_dword_ = data; + } + success = key.WriteValue(value_name_.c_str(), value_data_dword_); + } + if (success) { + LOG(INFO) << "overwritten value for " << value_name_; + status_ = VALUE_OVERWRITTEN; + result = true; + } else { + LOG(ERROR) << "failed to overwrite value for " << value_name_; + status_ = VALUE_UNCHANGED; + result = false; + } + } else { + LOG(INFO) << value_name_ << " exists. not changed "; + status_ = VALUE_UNCHANGED; + result = true; + } + } else { + bool success = true; + if (is_str_type_) { + success = key.WriteValue(value_name_.c_str(), value_data_str_.c_str()); + } else { + success = key.WriteValue(value_name_.c_str(), value_data_dword_); + } + if (success) { + LOG(INFO) << "created value for " << value_name_; + status_ = NEW_VALUE_CREATED; + result = true; + } else { + LOG(ERROR) << "failed to create value for " << value_name_; + status_ = VALUE_UNCHANGED; + result = false; + } + } + + key.Close(); + return result; +} + +void SetRegValueWorkItem::Rollback() { + if (status_ == SET_VALUE || status_ == VALUE_ROLL_BACK) + return; + + if (status_ == VALUE_UNCHANGED) { + status_ = VALUE_ROLL_BACK; + LOG(INFO) << "rollback: setting unchanged, nothing to do"; + return; + } + + RegKey key; + if (!key.Open(predefined_root_, key_path_.c_str(), + KEY_READ | KEY_SET_VALUE)) { + status_ = VALUE_ROLL_BACK; + LOG(INFO) << "rollback: can not open " << key_path_; + return; + } + + std::wstring result_str(L" failed"); + if (status_ == NEW_VALUE_CREATED) { + if (key.DeleteValue(value_name_.c_str())) + result_str.assign(L" succeeded"); + LOG(INFO) << "rollback: deleting " << value_name_ << result_str; + } else if (status_ == VALUE_OVERWRITTEN) { + // try restore the previous value + bool success = true; + if (is_str_type_) { + success = key.WriteValue(value_name_.c_str(), + previous_value_str_.c_str()); + } else { + success = key.WriteValue(value_name_.c_str(), previous_value_dword_); + } + if (success) + result_str.assign(L" succeeded"); + LOG(INFO) << "rollback: restoring " << value_name_ << result_str; + } else { + // Not reached. + } + + status_ = VALUE_ROLL_BACK; + key.Close(); + return; +} diff --git a/chrome/installer/util/set_reg_value_work_item.h b/chrome/installer/util/set_reg_value_work_item.h new file mode 100644 index 0000000..2b12509 --- /dev/null +++ b/chrome/installer/util/set_reg_value_work_item.h @@ -0,0 +1,101 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_INSTALLER_UTIL_SET_REG_VALUE_WORK_ITEM_H__ +#define CHROME_INSTALLER_UTIL_SET_REG_VALUE_WORK_ITEM_H__ + +#include <string> +#include <windows.h> +#include "chrome/installer/util/work_item.h" + +// A WorkItem subclass that sets a registry value with REG_SZ or REG_DWORD +// type at the specified path. The value is only set if the target key exists. +class SetRegValueWorkItem : public WorkItem { + public: + virtual ~SetRegValueWorkItem(); + + virtual bool Do(); + + virtual void Rollback(); + + private: + friend class WorkItem; + + enum SettingStatus { + // The status before Do is called. + SET_VALUE, + // One possible outcome after Do(). A new value is created under the key. + NEW_VALUE_CREATED, + // One possible outcome after Do(). The previous value under the key has + // been overwritten. + VALUE_OVERWRITTEN, + // One possible outcome after Do(). No change is applied, either + // because we are not allowed to overwrite the previous value, or due to + // some errors like the key does not exist. + VALUE_UNCHANGED, + // The status after Do and Rollback is called. + VALUE_ROLL_BACK + }; + + SetRegValueWorkItem(HKEY predefined_root, std::wstring key_path, + std::wstring value_name, std::wstring value_data, + bool overwrite); + + SetRegValueWorkItem(HKEY predefined_root, std::wstring key_path, + std::wstring value_name, DWORD value_data, + bool overwrite); + + // Root key of the target key under which the value is set. The root key can + // only be one of the predefined keys on Windows. + HKEY predefined_root_; + + // Path of the target key under which the value is set. + std::wstring key_path_; + + // Name of the value to be set. + std::wstring value_name_; + + // Data of the value to be set. + std::wstring value_data_str_; // if data is of type REG_SZ + DWORD value_data_dword_; // if data is of type REG_DWORD + + // Whether to overwrite the existing value under the target key. + bool overwrite_; + + // boolean that tells whether data value is of type REG_SZ. + bool is_str_type_; + + SettingStatus status_; + + // Data of the previous value. + std::wstring previous_value_str_; // if data is of type REG_SZ + DWORD previous_value_dword_; // if data is of type REG_DWORD +}; + +#endif // CHROME_INSTALLER_UTIL_SET_REG_VALUE_WORK_ITEM_H__ diff --git a/chrome/installer/util/set_reg_value_work_item_unittest.cc b/chrome/installer/util/set_reg_value_work_item_unittest.cc new file mode 100644 index 0000000..67e68e8 --- /dev/null +++ b/chrome/installer/util/set_reg_value_work_item_unittest.cc @@ -0,0 +1,263 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/registry.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/installer/util/set_reg_value_work_item.h" +#include "chrome/installer/util/work_item.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + wchar_t test_root[] = L"TempTemp"; + wchar_t data_str_1[] = L"data_111"; + wchar_t data_str_2[] = L"data_222"; + DWORD dword1 = 0; + DWORD dword2 = 1; + class SetRegValueWorkItemTest : public testing::Test { + protected: + virtual void SetUp() { + // Create a temporary key for testing + RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); + key.DeleteKey(test_root); + ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, test_root, KEY_READ)); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, test_root, KEY_READ)); + + // Create a log file + std::wstring log_file; + ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file)); + ASSERT_TRUE(file_util::PathExists(log_file)); + + logging::InitLogging(log_file.c_str(), + logging::LOG_ONLY_TO_FILE, + logging::LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE); + logging::SetMinLogLevel(logging::LOG_INFO); + } + virtual void TearDown() { + logging::CloseLogFile(); + // Clean up the temporary key + RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); + ASSERT_TRUE(key.DeleteKey(test_root)); + } + }; +}; + +// Write a new value without overwrite flag. The value should be set. +TEST_F(SetRegValueWorkItemTest, WriteNewNonOverwrite) { + RegKey key; + + std::wstring parent_key(test_root); + file_util::AppendToPath(&parent_key, L"WriteNewNonOverwrite"); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(), KEY_READ)); + + std::wstring name_str(L"name_str"); + std::wstring data_str(data_str_1); + scoped_ptr<SetRegValueWorkItem> work_item1( + WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key, + name_str, data_str, false)); + + std::wstring name_dword(L"name_dword"); + scoped_ptr<SetRegValueWorkItem> work_item2( + WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key, + name_dword, dword1, false)); + + EXPECT_TRUE(work_item1->Do()); + EXPECT_TRUE(work_item2->Do()); + + std::wstring read_out; + DWORD read_dword; + EXPECT_TRUE(key.ReadValue(name_str.c_str(), &read_out)); + EXPECT_TRUE(key.ReadValueDW(name_dword.c_str(), &read_dword)); + EXPECT_EQ(read_out, data_str_1); + EXPECT_EQ(read_dword, dword1); + + work_item1->Rollback(); + work_item2->Rollback(); + + // Rollback should delete the value. + EXPECT_FALSE(key.ValueExists(name_str.c_str())); + EXPECT_FALSE(key.ValueExists(name_dword.c_str())); +} + +// Write a new value with overwrite flag. The value should be set. +TEST_F(SetRegValueWorkItemTest, WriteNewOverwrite) { + RegKey key; + + std::wstring parent_key(test_root); + file_util::AppendToPath(&parent_key, L"WriteNewOverwrite"); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(), KEY_READ)); + + std::wstring name_str(L"name_str"); + std::wstring data_str(data_str_1); + scoped_ptr<SetRegValueWorkItem> work_item1( + WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key, + name_str, data_str, true)); + + std::wstring name_dword(L"name_dword"); + scoped_ptr<SetRegValueWorkItem> work_item2( + WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key, + name_dword, dword1, true)); + + EXPECT_TRUE(work_item1->Do()); + EXPECT_TRUE(work_item2->Do()); + + std::wstring read_out; + DWORD read_dword; + EXPECT_TRUE(key.ReadValue(name_str.c_str(), &read_out)); + EXPECT_TRUE(key.ReadValueDW(name_dword.c_str(), &read_dword)); + EXPECT_EQ(read_out, data_str_1); + EXPECT_EQ(read_dword, dword1); + + work_item1->Rollback(); + work_item2->Rollback(); + + // Rollback should delete the value. + EXPECT_FALSE(key.ValueExists(name_str.c_str())); + EXPECT_FALSE(key.ValueExists(name_dword.c_str())); +} + +// Write to an existing value without overwrite flag. There should be +// no change. +TEST_F(SetRegValueWorkItemTest, WriteExistingNonOverwrite) { + RegKey key; + + std::wstring parent_key(test_root); + file_util::AppendToPath(&parent_key, L"WriteExistingNonOverwrite"); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(), + KEY_READ | KEY_SET_VALUE)); + + // First test REG_SZ value. + // Write data to the value we are going to set. + std::wstring name(L"name_str"); + ASSERT_TRUE(key.WriteValue(name.c_str(), data_str_1)); + + std::wstring data(data_str_2); + scoped_ptr<SetRegValueWorkItem> work_item( + WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key, + name, data, false)); + EXPECT_TRUE(work_item->Do()); + + std::wstring read_out; + EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out)); + EXPECT_EQ(0, read_out.compare(data_str_1)); + + work_item->Rollback(); + EXPECT_TRUE(key.ValueExists(name.c_str())); + EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out)); + EXPECT_EQ(read_out, data_str_1); + + // Now test REG_DWORD value. + // Write data to the value we are going to set. + name.assign(L"name_dword"); + ASSERT_TRUE(key.WriteValue(name.c_str(), dword1)); + work_item.reset(WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, + parent_key, name, dword2, false)); + EXPECT_TRUE(work_item->Do()); + + DWORD read_dword; + EXPECT_TRUE(key.ReadValueDW(name.c_str(), &read_dword)); + EXPECT_EQ(read_dword, dword1); + + work_item->Rollback(); + EXPECT_TRUE(key.ValueExists(name.c_str())); + EXPECT_TRUE(key.ReadValueDW(name.c_str(), &read_dword)); + EXPECT_EQ(read_dword, dword1); +} + +// Write to an existing value with overwrite flag. The value should be +// overwritten. +TEST_F(SetRegValueWorkItemTest, WriteExistingOverwrite) { + RegKey key; + + std::wstring parent_key(test_root); + file_util::AppendToPath(&parent_key, L"WriteExistingOverwrite"); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(), + KEY_READ | KEY_SET_VALUE)); + + // First test REG_SZ value. + // Write data to the value we are going to set. + std::wstring name(L"name_str"); + ASSERT_TRUE(key.WriteValue(name.c_str(), data_str_1)); + + std::wstring data(data_str_2); + scoped_ptr<SetRegValueWorkItem> work_item( + WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key, + name, data, true)); + EXPECT_TRUE(work_item->Do()); + + std::wstring read_out; + EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out)); + EXPECT_EQ(0, read_out.compare(data_str_2)); + + work_item->Rollback(); + EXPECT_TRUE(key.ValueExists(name.c_str())); + EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out)); + EXPECT_EQ(read_out, data_str_1); + + // Now test REG_DWORD value. + // Write data to the value we are going to set. + name.assign(L"name_dword"); + ASSERT_TRUE(key.WriteValue(name.c_str(), dword1)); + work_item.reset(WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, + parent_key, name, dword2, true)); + EXPECT_TRUE(work_item->Do()); + + DWORD read_dword; + EXPECT_TRUE(key.ReadValueDW(name.c_str(), &read_dword)); + EXPECT_EQ(read_dword, dword2); + + work_item->Rollback(); + EXPECT_TRUE(key.ValueExists(name.c_str())); + EXPECT_TRUE(key.ReadValueDW(name.c_str(), &read_dword)); + EXPECT_EQ(read_dword, dword1); +} + +// Write a value to a non-existing key. This should fail. +TEST_F(SetRegValueWorkItemTest, WriteNonExistingKey) { + RegKey key; + + std::wstring parent_key(test_root); + file_util::AppendToPath(&parent_key, L"WriteNonExistingKey"); + + std::wstring name(L"name"); + std::wstring data(data_str_1); + scoped_ptr<SetRegValueWorkItem> work_item( + WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key, + name, data, false)); + EXPECT_FALSE(work_item->Do()); + + work_item.reset(WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, + parent_key, name, dword1, false)); + EXPECT_FALSE(work_item->Do()); +} diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc new file mode 100644 index 0000000..c8ebfc8 --- /dev/null +++ b/chrome/installer/util/shell_util.cc @@ -0,0 +1,376 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file defines functions that integrate Chrome in Windows shell. These +// functions can be used by Chrome as well as Chrome installer. All of the +// work is done by the local functions defined in anonymous namespace in +// this class. + +#include <windows.h> +#include <shellapi.h> +#include <shlobj.h> + +#include "shell_util.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/registry.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "base/win_util.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/installer/util/create_reg_key_work_item.h" +#include "chrome/installer/util/l10n_string_util.h" +#include "chrome/installer/util/set_reg_value_work_item.h" +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/work_item.h" + +#include "setup_strings.h" + +namespace { + +// This class represents a single registry entry. The objective is to +// encapsulate all the registry entries required for registering Chrome at one +// place. This class can not be instantiated outside the class and the objects +// of this class type can be obtained only by calling a static method of this +// class. +class RegistryEntry { + public: + // This method returns a list of all the registry entries that are needed + // to register Chrome. + static std::list<RegistryEntry*> GetAllEntries(const std::wstring& chrome_exe) { + std::list<RegistryEntry*> entries; + std::wstring icon_path(chrome_exe); + ShellUtil::GetChromeIcon(icon_path); + std::wstring quoted_exe_path = L"\"" + chrome_exe + L"\""; + std::wstring open_cmd = quoted_exe_path + L" \"%1\""; + + entries.push_front(new RegistryEntry(L"Software\\Classes\\ChromeHTML", + L"Chrome HTML")); + entries.push_front(new RegistryEntry( + L"Software\\Classes\\ChromeHTML\\DefaultIcon", icon_path)); + entries.push_front(new RegistryEntry( + L"Software\\Classes\\ChromeHTML\\shell\\open\\command", open_cmd)); + + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe", + installer_util::kApplicationName)); + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\shell\\open\\command", + quoted_exe_path)); + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\DefaultIcon", + icon_path)); + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\InstallInfo", + L"ReinstallCommand", + quoted_exe_path + L" --" + switches::kMakeDefaultBrowser)); + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\InstallInfo", + L"HideIconsCommand", + quoted_exe_path + L" --" + switches::kHideIcons)); + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\InstallInfo", + L"ShowIconsCommand", + quoted_exe_path + L" --" + switches::kShowIcons)); + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\InstallInfo", + L"IconsVisible", 1)); + + entries.push_front(new RegistryEntry( + ShellUtil::kRegRegisteredApplications, + installer_util::kApplicationName, + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities")); + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities", + L"ApplicationDescription", installer_util::kApplicationName)); + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities", + L"ApplicationIcon", icon_path)); + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities", + L"ApplicationName", installer_util::kApplicationName)); + + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities\\StartMenu", + L"StartMenuInternet", L"chrome.exe")); + for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) { + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities\\FileAssociations", + ShellUtil::kFileAssociations[i], ShellUtil::kChromeHTMLProgId)); + } + for (int i = 0; ShellUtil::kProtocolAssociations[i] != NULL; i++) { + entries.push_front(new RegistryEntry( + L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities\\URLAssociations", + ShellUtil::kProtocolAssociations[i], ShellUtil::kChromeHTMLProgId)); + } + return entries; + } + + // Generate work_item tasks required to create current regitry entry and + // add them to the given work item list. + void AddToWorkItemList(HKEY root, WorkItemList *items) { + items->AddCreateRegKeyWorkItem(root, _key_path); + if (_is_string) { + items->AddSetRegValueWorkItem(root, _key_path, _name, _value, true); + } else { + items->AddSetRegValueWorkItem(root, _key_path, _name, _int_value, true); + } + } + + // Check if the current registry entry exists in HKLM registry. + bool ExistsInHKLM() { + RegKey key(HKEY_LOCAL_MACHINE, _key_path.c_str()); + bool found = false; + if (_is_string) { + std::wstring read_value; + found = key.ReadValue(_name.c_str(), &read_value) && + read_value == _value; + } else { + DWORD read_value; + found = key.ReadValueDW(_name.c_str(), &read_value) && + read_value == _int_value; + } + key.Close(); + return found; + } + + private: + // Create a object that represent default value of a key + RegistryEntry(const std::wstring& key_path, const std::wstring& value) : + _key_path(key_path), _name(L""), _value(value), + _is_string(true) { + } + + // Create a object that represent a key of type REG_SZ + RegistryEntry(const std::wstring& key_path, const std::wstring& name, + const std::wstring& value) : _key_path(key_path), + _name(name), _value(value), _is_string(true) { + } + + // Create a object that represent a key of integer type + RegistryEntry(const std::wstring& key_path, const std::wstring& name, + DWORD value) : _key_path(key_path), + _name(name), _int_value(value), _is_string(false) { + } + + bool _is_string; // true if current registry entry is of type REG_SZ + std::wstring _key_path; // key path for the registry entry + std::wstring _name; // name of the registry entry + std::wstring _value; // string value (useful if _is_string = true) + DWORD _int_value; // integer value (useful if _is_string = false) +}; // class RegistryEntry + + +// This method checks if Chrome is already registered on the local machine. +// It gets all the required registry entries for Chrome and then checks if +// they exist in HKLM. Returns true if all the entries exist, otherwise false. +bool IsChromeRegistered(const std::wstring& chrome_exe) { + bool registered = true; + std::list<RegistryEntry*> entries = RegistryEntry::GetAllEntries(chrome_exe); + for (std::list<RegistryEntry*>::iterator itr = entries.begin(); + itr != entries.end(); ++itr) { + if (registered && !(*itr)->ExistsInHKLM()) + registered = false; + delete (*itr); + } + LOG(INFO) << "Check for Chrome registeration returned " << registered; + return registered; +} + + +// This method creates the registry entries required for Add/Remove Programs-> +// Set Program Access and Defaults, Start->Default Programs on Windows Vista +// and Chrome ProgIds for file extension and protocol handler. root_key is +// the root registry (HKLM or HKCU). +bool SetAccessDefaultRegEntries(HKEY root_key, + const std::wstring& chrome_exe) { + LOG(INFO) << "Registering Chrome browser " << chrome_exe; + // Create a list of registry entries work items so that we can rollback + // in case of problem. + scoped_ptr<WorkItemList> items(WorkItem::CreateWorkItemList()); + + std::list<RegistryEntry*> entries = RegistryEntry::GetAllEntries(chrome_exe); + for (std::list<RegistryEntry*>::iterator itr = entries.begin(); + itr != entries.end(); ++itr) { + (*itr)->AddToWorkItemList(root_key, items.get()); + delete (*itr); + } + + // Apply all the registry changes and if there is a problem, rollback. + if (!items->Do()) { + LOG(ERROR) << "Failed to add Chrome to Set Program Access and Defaults"; + items->Rollback(); + return false; + } + + return true; +} + + +// This method registers Chrome on Vista. It checks if we are currently +// running as admin and if not, it launches setup.exe as administrator which +// will show user standard Vista elevation prompt. If user accepts it +// the new process will make the necessary changes and return SUCCESS that +// we capture and return. +ShellUtil::RegisterStatus RegisterOnVista(const std::wstring& chrome_exe, + bool skip_if_not_admin) { + if (IsUserAnAdmin() && + SetAccessDefaultRegEntries(HKEY_LOCAL_MACHINE, chrome_exe)) + return ShellUtil::SUCCESS; + + if (!skip_if_not_admin) { + std::wstring exe_path(file_util::GetDirectoryFromPath(chrome_exe)); + file_util::AppendToPath(&exe_path, installer_util::kSetupExe); + if (!file_util::PathExists(exe_path)) { + RegKey key(HKEY_CURRENT_USER, installer_util::kUninstallRegPath); + key.ReadValue(installer_util::kUninstallStringField, &exe_path); + exe_path = exe_path.substr(0, exe_path.find_first_of(L" --")); + TrimString(exe_path, L" \"", &exe_path); + } + if (file_util::PathExists(exe_path)) { + SHELLEXECUTEINFO info = {0}; + info.cbSize = sizeof(SHELLEXECUTEINFO); + info.fMask = SEE_MASK_NOCLOSEPROCESS; + info.lpVerb = L"runas"; + info.lpFile = exe_path.c_str(); + std::wstring params(L"--"); + params.append(installer_util::switches::kRegisterChromeBrowser); + params.append(L"=\"" + chrome_exe + L"\""); + info.lpParameters = params.c_str(); + info.nShow = SW_SHOW; + if (::ShellExecuteEx(&info)) { + ::WaitForSingleObject(info.hProcess, INFINITE); + DWORD ret_val = ShellUtil::SUCCESS; + if (::GetExitCodeProcess(info.hProcess, &ret_val) && + (ret_val == ShellUtil::SUCCESS)) + return ShellUtil::SUCCESS; + } + } + } + return ShellUtil::FAILURE; +} + +} // namespace + + +const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon"; +const wchar_t* ShellUtil::kRegShellPath = L"\\shell"; +const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command"; +const wchar_t* ShellUtil::kRegStartMenuInternet = + L"Software\\Clients\\StartMenuInternet"; +const wchar_t* ShellUtil::kRegClasses = L"Software\\Classes"; +const wchar_t* ShellUtil::kRegRegisteredApplications = + L"Software\\RegisteredApplications"; +const wchar_t* ShellUtil::kRegShellChromeHTML = L"\\shell\\ChromeHTML"; +const wchar_t* ShellUtil::kRegShellChromeHTMLCommand = + L"\\shell\\ChromeHTML\\command"; + +const wchar_t* ShellUtil::kChromeHTMLProgId = L"ChromeHTML"; +const wchar_t* ShellUtil::kFileAssociations[] = {L".htm", L".html", L".shtml", + L".xht", L".xhtml", NULL}; +const wchar_t* ShellUtil::kProtocolAssociations[] = {L"ftp", L"http", L"https", + NULL}; + + +ShellUtil::RegisterStatus ShellUtil::AddChromeToSetAccessDefaults( + const std::wstring& chrome_exe, bool skip_if_not_admin) { + if (IsChromeRegistered(chrome_exe)) + return ShellUtil::SUCCESS; + + if (win_util::GetWinVersion() == win_util::WINVERSION_VISTA) + return RegisterOnVista(chrome_exe, skip_if_not_admin); + + // Try adding these entries to HKLM first and if that fails try adding + // to HKCU. + if (SetAccessDefaultRegEntries(HKEY_LOCAL_MACHINE, chrome_exe)) + return ShellUtil::SUCCESS; + + if (!skip_if_not_admin && + SetAccessDefaultRegEntries(HKEY_CURRENT_USER, chrome_exe)) + return ShellUtil::REGISTERED_PER_USER; + + return ShellUtil::FAILURE; +} + +bool ShellUtil::GetChromeIcon(std::wstring& chrome_icon) { + if (chrome_icon.empty()) + return false; + + chrome_icon.append(L",0"); + return true; +} + +bool ShellUtil::GetChromeShortcutName(std::wstring* shortcut) { + shortcut->assign(installer_util::GetLocalizedString(IDS_PRODUCT_NAME_BASE)); + shortcut->append(L".lnk"); + return true; +} + +bool ShellUtil::GetDesktopPath(std::wstring* path) { + wchar_t desktop[MAX_PATH]; + if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, + desktop))) + return false; + *path = desktop; + return true; +} + +bool ShellUtil::GetQuickLaunchPath(std::wstring* path) { + if (!PathService::Get(base::DIR_APP_DATA, path)) + return false; + // This path works on Vista as well. + file_util::AppendToPath(path, L"Microsoft\\Internet Explorer\\Quick Launch"); + return true; +} + +bool ShellUtil::UpdateChromeShortcut(const std::wstring& chrome_exe, + const std::wstring& shortcut, + bool create_new) { + std::wstring chrome_path = file_util::GetDirectoryFromPath(chrome_exe); + if (create_new) { + return file_util::CreateShortcutLink(chrome_exe.c_str(), // target + shortcut.c_str(), // shortcut + chrome_path.c_str(), // working dir + NULL, // arguments + NULL, // description + chrome_exe.c_str(), // icon file + 0); // icon index + } else { + return file_util::UpdateShortcutLink(chrome_exe.c_str(), // target + shortcut.c_str(), // shortcut + chrome_path.c_str(), // working dir + NULL, // arguments + NULL, // description + chrome_exe.c_str(), // icon file + 0); // icon index + } +} diff --git a/chrome/installer/util/shell_util.h b/chrome/installer/util/shell_util.h new file mode 100644 index 0000000..d741a76 --- /dev/null +++ b/chrome/installer/util/shell_util.h @@ -0,0 +1,145 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file declares methods that are useful for integrating Chrome in +// Windows shell. These methods are all static and currently part of +// ShellUtil class. + +#ifndef CHROME_INSTALLER_UTIL_SHELL_UTIL_H__ +#define CHROME_INSTALLER_UTIL_SHELL_UTIL_H__ + +#include <windows.h> +#include <string> + +#include "base/basictypes.h" +#include "chrome/installer/util/work_item_list.h" + +// This is a utility class that provides common shell integration methods +// that can be used by installer as well as Chrome. +class ShellUtil { + public: + // Return value of AddChromeToSetAccessDefaults. + enum RegisterStatus { + SUCCESS, // Registration of Chrome successful (in HKLM) + FAILURE, // Registration failed (no changes made) + REGISTERED_PER_USER // Registered Chrome as per user (in HKCU) + }; + + // Relative path of DefaultIcon registry entry (prefixed with '\'). + static const wchar_t* kRegDefaultIcon; + + // Relative path of "shell" registry key. + static const wchar_t* kRegShellPath; + + // Relative path of shell open command in Windows registry + // (i.e. \\shell\\open\\command). + static const wchar_t* kRegShellOpen; + + // Relative path of registry key under which applications need to register + // to control Windows Start menu links. + static const wchar_t* kRegStartMenuInternet; + + // Relative path of Classes registry entry under which file associations + // are added on Windows. + static const wchar_t* kRegClasses; + + // Relative path of RegisteredApplications registry entry under which + // we add Chrome as a Windows application + static const wchar_t* kRegRegisteredApplications; + + // Name that we give to Chrome file association handler ProgId. + static const wchar_t* kChromeHTMLProgId; + + // Relative path of shell Chrome Progid in Windows registry + // (i.e. \\shell\\ChromeHTML). + static const wchar_t* kRegShellChromeHTML; + + // Relative path of shell Chrome Progid command in Windows registry + // (i.e. \\shell\\ChromeHTML\\command). + static const wchar_t* kRegShellChromeHTMLCommand; + + // File extensions that Chrome registers itself for. + static const wchar_t* kFileAssociations[]; + + // Protocols that Chrome registers itself for. + static const wchar_t* kProtocolAssociations[]; + + // This method adds Chrome to the list that shows up in Add/Remove Programs-> + // Set Program Access and Defaults and also creates Chrome ProgIds under + // Software\Classes. This method requires write access to HKLM so is just + // best effort deal. If write to HKLM fails and skip_if_not_admin is false, + // this method will: + // - add the ProgId entries to HKCU on XP. HKCU entries will not make + // Chrome show in Set Program Access and Defaults but they are still useful + // because we can make Chrome run when user clicks on http link or html + // file. + // - will try to launch setup.exe with admin priviledges on Vista to do + // these tasks. Users will see standard Vista elevation prompt and if they + // enter the right credentials, the write operation will work. + // Currently skip_if_not_admin is false only when user tries to make Chrome + // default browser and Chrome is not registered on the machine. + // + // chrome_exe: full path to chrome.exe. + // skip_if_not_admin: if false will make this method try alternate methods + // as described above. + static RegisterStatus AddChromeToSetAccessDefaults( + const std::wstring& chrome_exe, bool skip_if_not_admin); + + // This method appends the Chrome icon index inside chrome.exe to the + // chrome.exe path passed in as input, to generate the full path for + // Chrome icon that can be used as value for Windows registry keys. + // chrome_icon: full path to chrome.exe. + static bool GetChromeIcon(std::wstring& chrome_icon); + + // Returns the localized name of Chrome shortcut. + static bool GetChromeShortcutName(std::wstring* shortcut); + + // Gets the desktop path for the current user and returns it in 'path' + // argument. Return true if successful, otherwise returns false. + static bool GetDesktopPath(std::wstring* path); + + // Gets the Quick Launch shortcuts path for the current user and + // returns it in 'path' argument. Return true if successful, otherwise + // returns false. + static bool GetQuickLaunchPath(std::wstring* path); + + // Updates shortcut (or creates a new shortcut) at destination given by + // shortcut to a target given by chrome_exe. The arguments is left NULL + // for the target and icon is set as icon at index 0 from exe. + // If create_new is set to true, the function will create a new shortcut if + // if doesn't exist. + static bool UpdateChromeShortcut(const std::wstring& chrome_exe, + const std::wstring& shortcut, + bool create_new); + private: + DISALLOW_EVIL_CONSTRUCTORS(ShellUtil); +}; + + +#endif // CHROME_INSTALLER_UTIL_SHELL_UTIL_H__ diff --git a/chrome/installer/util/using_util.vsprops b/chrome/installer/util/using_util.vsprops new file mode 100644 index 0000000..fe84cd9 --- /dev/null +++ b/chrome/installer/util/using_util.vsprops @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="using_util" + > + <Tool + Name="VCLinkerTool" + AdditionalDependencies="shlwapi.lib" + /> +</VisualStudioPropertySheet> diff --git a/chrome/installer/util/util.vcproj b/chrome/installer/util/util.vcproj new file mode 100644 index 0000000..a63254c --- /dev/null +++ b/chrome/installer/util/util.vcproj @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="util" + ProjectGUID="{EFBB1436-A63F-4CD8-9E99-B89226E782EC}" + RootNamespace="util" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)common\common.vsprops;$(SolutionDir)..\third_party\lzma_sdk\using_lzma_sdk.vsprops;prebuild\util_prebuild.vsprops" + > + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCLibrarianTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)common\common.vsprops;$(SolutionDir)..\third_party\lzma_sdk\using_lzma_sdk.vsprops;prebuild\util_prebuild.vsprops" + > + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCLibrarianTool" + /> + </Configuration> + </Configurations> + <Files> + <File + RelativePath="..\..\app\google_update_settings.cc" + > + </File> + <File + RelativePath="..\..\app\google_update_settings.h" + > + </File> + <File + RelativePath="copy_tree_work_item.cc" + > + </File> + <File + RelativePath="copy_tree_work_item.h" + > + </File> + <File + RelativePath="create_dir_work_item.cc" + > + </File> + <File + RelativePath="create_dir_work_item.h" + > + </File> + <File + RelativePath="create_reg_key_work_item.cc" + > + </File> + <File + RelativePath="create_reg_key_work_item.h" + > + </File> + <File + RelativePath="delete_tree_work_item.cc" + > + </File> + <File + RelativePath="delete_tree_work_item.h" + > + </File> + <File + RelativePath="helper.cc" + > + </File> + <File + RelativePath="helper.h" + > + </File> + <File + RelativePath="install_util.cc" + > + </File> + <File + RelativePath="install_util.h" + > + </File> + <File + RelativePath="l10n_string_util.cc" + > + </File> + <File + RelativePath="l10n_string_util.h" + > + </File> + <File + RelativePath="logging_installer.cc" + > + </File> + <File + RelativePath="logging_installer.h" + > + </File> + <File + RelativePath=".\lzma_util.cc" + > + </File> + <File + RelativePath=".\lzma_util.h" + > + </File> + <File + RelativePath=".\google_update_constants.cc" + > + </File> + <File + RelativePath=".\google_update_constants.h" + > + </File> + <File + RelativePath="set_reg_value_work_item.cc" + > + </File> + <File + RelativePath="set_reg_value_work_item.h" + > + </File> + <File + RelativePath=".\shell_util.cc" + > + </File> + <File + RelativePath=".\shell_util.h" + > + </File> + <File + RelativePath="util_constants.cc" + > + </File> + <File + RelativePath="util_constants.h" + > + </File> + <File + RelativePath="version.cc" + > + </File> + <File + RelativePath="version.h" + > + </File> + <File + RelativePath="work_item.cc" + > + </File> + <File + RelativePath="work_item.h" + > + </File> + <File + RelativePath="work_item_list.cc" + > + </File> + <File + RelativePath="work_item_list.h" + > + </File> + </Files> +</VisualStudioProject> diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc new file mode 100644 index 0000000..9833671 --- /dev/null +++ b/chrome/installer/util/util_constants.cc @@ -0,0 +1,82 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/installer/util/util_constants.h" + +namespace installer_util { + +namespace switches { + +// Disable logging +const wchar_t kDisableLogging[] = L"disable-logging"; + +// Enable logging at the error level. This is the default behavior. +const wchar_t kEnableLogging[] = L"enable-logging"; + +// Specify the file path of Chrome archive for install. +const wchar_t kInstallArchive[] = L"install-archive"; + +// If present, specify file path to write logging info. +const wchar_t kLogFile[] = L"log-file"; + +// Register Chrome as a valid browser on the current sytem. This option +// requires that setup.exe is running as admin. If this option is specified, +// options kInstallArchive and kUninstall are ignored. +const wchar_t kRegisterChromeBrowser[] = L"register-chrome-browser"; + +// By default we remove all shared (between users) files, registry entries etc +// during uninstall. If this option is specified together with kUninstall option +// we do not clean up shared entries otherwise this option is ignored. +const wchar_t kDoNotRemoveSharedItems[] = L"do-not-remove-shared-items"; + +// Install Chrome to system wise location. The default is per user install. +const wchar_t kSystemInstall[] = L"system-install"; + +// If present, setup will uninstall chrome. +const wchar_t kUninstall[] = L"uninstall"; + +// Enable verbose logging (info level). +const wchar_t kVerboseLogging[] = L"verbose-logging"; + +} // namespace switches + +const wchar_t kInstallBinaryDir[] = L"Application"; +const wchar_t kInstallGoogleDir[] = L"Google"; +const wchar_t kChrome[] = L"Chrome"; +const wchar_t kChromeExe[] = L"chrome.exe"; +const wchar_t kChromeDll[] = L"chrome.dll"; + +const wchar_t kPublisherName[] = L"Google"; +const wchar_t kApplicationName[] = L"Google Chrome"; +const wchar_t kSetupExe[] = L"setup.exe"; + +const wchar_t kUninstallRegPath[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Chrome"; +const wchar_t kUninstallStringField[] = L"UninstallString"; +const wchar_t kUninstallDisplayNameField[] = L"DisplayName"; +} // namespace installer_util diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h new file mode 100644 index 0000000..af9596f --- /dev/null +++ b/chrome/installer/util/util_constants.h @@ -0,0 +1,88 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Defines all install related constants that need to be used by Chrome as +// well as Chrome Installer. + +#ifndef CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H__ +#define CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H__ + +namespace installer_util { + +// Return status of installer +enum InstallStatus { + FIRST_INSTALL_SUCCESS, // Successfully installed Chrome for the first time + INSTALL_REPAIRED, // Same version reinstalled for repair + NEW_VERSION_UPDATED, // Chrome successfully updated to new version + HIGHER_VERSION_EXISTS, // Higher version of Chrome already exists + INSTALL_FAILED, // Install/update failed + OS_NOT_SUPPORTED, // Current OS not supported + OS_ERROR, // OS API call failed + TEMP_DIR_FAILED, // Unable to get Temp directory + UNCOMPRESSION_FAILED, // Failed to uncompress Chrome archive + INVALID_ARCHIVE, // Something wrong with the installer archive + CHROME_NOT_INSTALLED, // Chrome not installed (returned in case of uninstall) + CHROME_RUNNING, // Chrome currently running (when trying to uninstall) + UNINSTALL_CONFIRMED, // User has confirmed Chrome uninstall + UNINSTALL_SUCCESSFUL, // Chrome successfully uninstalled + UNINSTALL_FAILED, // Chrome uninstallation failed + UNINSTALL_CANCELLED, // User cancelled Chrome uninstallation + UNKNOWN_STATUS, // Unknown status (this should never happen) +}; + +namespace switches { +extern const wchar_t kDisableLogging[]; +extern const wchar_t kEnableLogging[]; +extern const wchar_t kInstallArchive[]; +extern const wchar_t kLogFile[]; +extern const wchar_t kRegisterChromeBrowser[]; +extern const wchar_t kDoNotRemoveSharedItems[]; +extern const wchar_t kSystemInstall[]; +extern const wchar_t kUninstall[]; +extern const wchar_t kVerboseLogging[]; +} // namespace switches + +extern const wchar_t kInstallBinaryDir[]; +extern const wchar_t kInstallGoogleDir[]; +extern const wchar_t kChrome[]; +extern const wchar_t kChromeExe[]; +extern const wchar_t kChromeDll[]; + +// Bug 1214772 - these should be removed for public beta and replaced with +// a localized string. +extern const wchar_t kPublisherName[]; +extern const wchar_t kApplicationName[]; +extern const wchar_t kSetupExe[]; + +extern const wchar_t kUninstallRegPath[]; +extern const wchar_t kUninstallStringField[]; +extern const wchar_t kUninstallDisplayNameField[]; +} // namespace installer_util + +#endif // CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H__ diff --git a/chrome/installer/util/version.cc b/chrome/installer/util/version.cc new file mode 100644 index 0000000..925c950 --- /dev/null +++ b/chrome/installer/util/version.cc @@ -0,0 +1,71 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <vector> + +#include "base/string_util.h" +#include "chrome/installer/util/version.h" + +installer::Version::Version(int64 major, int64 minor, int64 build, + int64 patch) : + major_(major), minor_(minor), build_(build), patch_(patch) { + version_str_.append(Int64ToWString(major_)); + version_str_.append(L"."); + version_str_.append(Int64ToWString(minor_)); + version_str_.append(L"."); + version_str_.append(Int64ToWString(build_)); + version_str_.append(L"."); + version_str_.append(Int64ToWString(patch_)); +} + +installer::Version::~Version() { +} + +bool installer::Version::IsHigherThan(const installer::Version* other) const { + return ((major_ > other->major_) || + ((major_ == other->major_) && (minor_ > other->minor_)) || + ((major_ == other->major_) && (minor_ == other->minor_) + && (build_ > other->build_)) || + ((major_ == other->major_) && (minor_ == other->minor_) + && (build_ == other->build_) + && (patch_ > other->patch_))); +} + +installer::Version* installer::Version::GetVersionFromString( + std::wstring version_str) { + std::vector<std::wstring> numbers; + SplitString(version_str, '.', &numbers); + + if (numbers.size() != 4) { + return NULL; + } + + return new Version(StringToInt64(numbers[0]), StringToInt64(numbers[1]), + StringToInt64(numbers[2]), StringToInt64(numbers[3])); +} diff --git a/chrome/installer/util/version.h b/chrome/installer/util/version.h new file mode 100644 index 0000000..fba1f9a --- /dev/null +++ b/chrome/installer/util/version.h @@ -0,0 +1,71 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_INSTALLER_UTIL_VERSION_H__ +#define CHROME_INSTALLER_UTIL_VERSION_H__ + +#include <string> + +#include "base/basictypes.h" + +namespace installer { + +class Version { +public: + virtual ~Version(); + + // Check if the current version is higher than the version object passed + // as parameter + bool IsHigherThan(const Version* other) const; + + // Return the string representation of this version + const std::wstring& GetString() const { + return version_str_; + } + + // Assume that the version string is specified by four integers separated + // by character '.'. Return NULL if string is not of this format. + // Caller is responsible for freeing the Version object once done. + static Version* GetVersionFromString(std::wstring version_str); + +private: + int64 major_; + int64 minor_; + int64 build_; + int64 patch_; + std::wstring version_str_; + + // Classes outside this file do not have any need to create objects of + // this type so declare constructor as private. + Version(int64 major, int64 minor, int64 build, int64 patch); +}; + +} // namespace installer + +#endif diff --git a/chrome/installer/util/work_item.cc b/chrome/installer/util/work_item.cc new file mode 100644 index 0000000..c273cf3 --- /dev/null +++ b/chrome/installer/util/work_item.cc @@ -0,0 +1,85 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/installer/util/work_item.h" +#include "chrome/installer/util/copy_tree_work_item.h" +#include "chrome/installer/util/create_dir_work_item.h" +#include "chrome/installer/util/create_reg_key_work_item.h" +#include "chrome/installer/util/delete_tree_work_item.h" +#include "chrome/installer/util/set_reg_value_work_item.h" +#include "chrome/installer/util/work_item_list.h" + +WorkItem::WorkItem() { +} + +WorkItem::~WorkItem() { +} + +CopyTreeWorkItem* WorkItem::CreateCopyTreeWorkItem( + std::wstring source_path, std::wstring dest_path, std::wstring temp_dir, + CopyOverWriteOption overwrite_option, std::wstring alternative_path) { + return new CopyTreeWorkItem(source_path, dest_path, temp_dir, + overwrite_option, alternative_path); +} + +CreateDirWorkItem* WorkItem::CreateCreateDirWorkItem(std::wstring path) { + return new CreateDirWorkItem(path); +} + +CreateRegKeyWorkItem* WorkItem::CreateCreateRegKeyWorkItem( + HKEY predefined_root, std::wstring path) { + return new CreateRegKeyWorkItem(predefined_root, path); +} + +DeleteTreeWorkItem* WorkItem::CreateDeleteTreeWorkItem(std::wstring root_path, + std::wstring key_path) { + return new DeleteTreeWorkItem(root_path, key_path); +} + +SetRegValueWorkItem* WorkItem::CreateSetRegValueWorkItem( + HKEY predefined_root, std::wstring key_path, + std::wstring value_name, std::wstring value_data, bool overwrite) { + return new SetRegValueWorkItem(predefined_root, key_path, + value_name, value_data, overwrite); +} + +SetRegValueWorkItem* WorkItem::CreateSetRegValueWorkItem( + HKEY predefined_root, std::wstring key_path, + std::wstring value_name, DWORD value_data, bool overwrite) { + return new SetRegValueWorkItem(predefined_root, key_path, + value_name, value_data, overwrite); +} + +WorkItemList* WorkItem::CreateWorkItemList() { + return new WorkItemList(); +} + +std::wstring WorkItem::Dump() { + return std::wstring(L"Work Item"); +} diff --git a/chrome/installer/util/work_item.h b/chrome/installer/util/work_item.h new file mode 100644 index 0000000..c167cf3 --- /dev/null +++ b/chrome/installer/util/work_item.h @@ -0,0 +1,124 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Base class for managing an action of a sequence of actions to be carried +// out during install/update/uninstall. Supports rollback of actions if this +// process fails. + +#ifndef CHROME_INSTALLER_UTIL_WORK_ITEM_H__ +#define CHROME_INSTALLER_UTIL_WORK_ITEM_H__ + +#include <string> +#include <windows.h> + +class CopyTreeWorkItem; +class CreateDirWorkItem; +class CreateRegKeyWorkItem; +class DeleteTreeWorkItem; +class SetRegValueWorkItem; +class WorkItemList; + +// A base class that defines APIs to perform/rollback an action or a +// sequence of actions during install/update/uninstall. +class WorkItem { + public: + // Possible states + typedef enum CopyOverWriteOption { + ALWAYS, // Always overwrite regardless of what existed before. + NEVER, // Not used currently. + IF_DIFFERENT, // Overwrite if different. Currently only applies to file. + RENAME_IF_IN_USE // Copy to a new path instead of overwriting (only files). + }; + + virtual ~WorkItem(); + + // Create a CopyTreeWorkItem that recursively copies a file system hierarchy + // from source path to destination path. If overwrite_option is ALWAYS, the + // created CopyTreeWorkItem always overwrites files. If overwrite_option is + // RENAME_IF_IN_USE, file is copied with an alternate name specified by + // alternative_path. + static CopyTreeWorkItem* CreateCopyTreeWorkItem(std::wstring source_path, + std::wstring dest_path, std::wstring temp_dir, + CopyOverWriteOption overwrite_option, + std::wstring alternative_path = L""); + + // Create a CreateDirWorkItem that creates a directory at the given path. + static CreateDirWorkItem* CreateCreateDirWorkItem(std::wstring path); + + // Create a CreateRegKeyWorkItem that creates a registry key at the given + // path. + static CreateRegKeyWorkItem* CreateCreateRegKeyWorkItem( + HKEY predefined_root, std::wstring path); + + // Create a DeleteTreeWorkItem that recursively deletes a file system + // hierarchy at the given root path. A key file can be optionally specified + // by key_path. + static DeleteTreeWorkItem* CreateDeleteTreeWorkItem(std::wstring root_path, + std::wstring key_path); + + // Create a SetRegValueWorkItem that sets a registry value with REG_SZ type + // at the key with specified path. + static SetRegValueWorkItem* CreateSetRegValueWorkItem( + HKEY predefined_root, std::wstring key_path, + std::wstring value_name, std::wstring value_data, bool overwrite); + + // Create a SetRegValueWorkItem that sets a registry value with REG_DWORD type + // at the key with specified path. + static SetRegValueWorkItem* CreateSetRegValueWorkItem( + HKEY predefined_root, std::wstring key_path, + std::wstring value_name, DWORD value_data, bool overwrite); + + // Create an empty WorkItemList. A WorkItemList can recursively contains + // a list of WorkItems. + static WorkItemList* CreateWorkItemList(); + + // Perform the actions of WorkItem. Returns true if success, returns false + // otherwise. + // If the WorkItem is transactional, then Do() is done as a transaction. + // If it returns false, there will be no change on the system. + virtual bool Do() = 0; + + // Rollback any actions previously carried out by this WorkItem. If the + // WorkItem is transactional, then the previous actions can be fully + // rolled back. If the WorkItem is non-transactional, the rollback is a + // best effort. + virtual void Rollback() = 0; + + // Return true if the WorkItem is transactional, return false if + // non-transactional. + virtual bool IsTransactional() { return false; } + + // For diagnostics. + virtual std::wstring Dump(); + + protected: + WorkItem(); +}; + +#endif // CHROME_INSTALLER_UTIL_WORK_ITEM_H__ diff --git a/chrome/installer/util/work_item_list.cc b/chrome/installer/util/work_item_list.cc new file mode 100644 index 0000000..d9c4b54 --- /dev/null +++ b/chrome/installer/util/work_item_list.cc @@ -0,0 +1,142 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/installer/util/logging_installer.h" +#include "chrome/installer/util/work_item_list.h" + +WorkItemList::~WorkItemList() { + for (WorkItemIterator itr = list_.begin(); itr != list_.end(); ++itr) { + delete (*itr); + } + for (WorkItemIterator itr = executed_list_.begin(); + itr != executed_list_.end(); ++itr) { + delete (*itr); + } +} + +WorkItemList::WorkItemList() + : status_(ADD_ITEM) { +} + +bool WorkItemList::Do() { + if (status_ != ADD_ITEM) + return false; + + bool result = true; + while (!list_.empty()) { + WorkItem* work_item = list_.front(); + list_.pop_front(); + executed_list_.push_front(work_item); + if (!work_item->Do()) { + LOG(ERROR) << "list execution failed"; + result = false; + break; + } + } + + if (result) + LOG(INFO) << "list execution succeeded"; + + status_ = LIST_EXECUTED; + return result; +} + +void WorkItemList::Rollback() { + if (status_ != LIST_EXECUTED) + return; + + for (WorkItemIterator itr = executed_list_.begin(); + itr != executed_list_.end(); ++itr) { + (*itr)->Rollback(); + } + + status_ = LIST_ROLLED_BACK; + return; +} + +bool WorkItemList::AddWorkItem(WorkItem* work_item) { + if (status_ != ADD_ITEM) + return false; + + list_.push_back(work_item); + return true; +} + +bool WorkItemList::AddCopyTreeWorkItem(std::wstring source_path, + std::wstring dest_path, + std::wstring temp_dir, + CopyOverWriteOption overwrite_option, + std::wstring alternative_path) { + WorkItem* item = reinterpret_cast<WorkItem*>( + WorkItem::CreateCopyTreeWorkItem(source_path, dest_path, temp_dir, + overwrite_option, alternative_path)); + return AddWorkItem(item); +} + +bool WorkItemList::AddCreateDirWorkItem(std::wstring path) { + WorkItem* item = reinterpret_cast<WorkItem*>( + WorkItem::CreateCreateDirWorkItem(path)); + return AddWorkItem(item); +} + +bool WorkItemList::AddCreateRegKeyWorkItem(HKEY predefined_root, + std::wstring path) { + WorkItem* item = reinterpret_cast<WorkItem*>( + WorkItem::CreateCreateRegKeyWorkItem(predefined_root, path)); + return AddWorkItem(item); +} + +bool WorkItemList::AddDeleteTreeWorkItem(std::wstring root_path, + std::wstring key_path) { + WorkItem* item = reinterpret_cast<WorkItem*>( + WorkItem::CreateDeleteTreeWorkItem(root_path, key_path)); + return AddWorkItem(item); +} + +bool WorkItemList::AddSetRegValueWorkItem(HKEY predefined_root, + std::wstring key_path, + std::wstring value_name, + std::wstring value_data, + bool overwrite) { + WorkItem* item = reinterpret_cast<WorkItem*>( + WorkItem::CreateSetRegValueWorkItem(predefined_root, key_path, value_name, + value_data, overwrite)); + return AddWorkItem(item); +} + +bool WorkItemList::AddSetRegValueWorkItem(HKEY predefined_root, + std::wstring key_path, + std::wstring value_name, + DWORD value_data, + bool overwrite) { + WorkItem* item = reinterpret_cast<WorkItem*>( + WorkItem::CreateSetRegValueWorkItem(predefined_root, key_path, value_name, + value_data, overwrite)); + return AddWorkItem(item); +} diff --git a/chrome/installer/util/work_item_list.h b/chrome/installer/util/work_item_list.h new file mode 100644 index 0000000..a33a21888 --- /dev/null +++ b/chrome/installer/util/work_item_list.h @@ -0,0 +1,116 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_INSTALLER_UTIL_WORK_ITEM_LIST_H__ +#define CHROME_INSTALLER_UTIL_WORK_ITEM_LIST_H__ + +#include <list> +#include <string> +#include <windows.h> +#include "chrome/installer/util/work_item.h" + +// A WorkItem subclass that recursively contains a list of WorkItems. Thus it +// provides functionalities to carry out or roll back the sequence of actions +// defined by the list of WorkItems it contains. +// The WorkItems are executed in the same order as they are added to the list. +class WorkItemList : public WorkItem { + public: + virtual ~WorkItemList(); + + // Execute the WorkItems in the same order as they are added to the list. + // It aborts as soon as one WorkItem fails. + virtual bool Do(); + + // Rollback the WorkItems in the reverse order as they are executed. + virtual void Rollback(); + + // Add a WorkItem to the list. Return true if the WorkItem is successfully + // added. Return false otherwise. + // A WorkItem can only be added to the list before the list's DO() is called. + // Once a WorkItem is added to the list. The list owns the WorkItem. + bool AddWorkItem(WorkItem* work_item); + + // Add a CopyTreeWorkItem to the list of work items. + bool AddCopyTreeWorkItem(std::wstring source_path, std::wstring dest_path, + std::wstring temp_dir, + CopyOverWriteOption overwrite_option, + std::wstring alternative_path = L""); + + // Add a CreateDirWorkItem that creates a directory at the given path. + bool AddCreateDirWorkItem(std::wstring path); + + // Add a CreateRegKeyWorkItem that creates a registry key at the given + // path. + bool AddCreateRegKeyWorkItem(HKEY predefined_root, std::wstring path); + + // Add a DeleteTreeWorkItem that recursively deletes a file system + // hierarchy at the given root path. A key file can be optionally specified + // by key_path. + bool AddDeleteTreeWorkItem(std::wstring root_path, std::wstring key_path); + + // Add a SetRegValueWorkItem that sets a registry value with REG_SZ type + // at the key with specified path. + bool AddSetRegValueWorkItem(HKEY predefined_root, std::wstring key_path, + std::wstring value_name, std::wstring value_data, + bool overwrite); + + // Add a SetRegValueWorkItem that sets a registry value with REG_DWORD type + // at the key with specified path. + bool AddSetRegValueWorkItem(HKEY predefined_root, std::wstring key_path, + std::wstring value_name, DWORD value_data, + bool overwrite); + + private: + friend class WorkItem; + + typedef std::list<WorkItem*> WorkItems; + typedef WorkItems::iterator WorkItemIterator; + + enum ListStatus { + // List has not been executed. Ok to add new WorkItem. + ADD_ITEM, + // List has been executed. Can not add new WorkItem. + LIST_EXECUTED, + // List has been executed and rolled back. No further action is acceptable. + LIST_ROLLED_BACK + }; + + WorkItemList(); + + ListStatus status_; + + // The list of WorkItems, in the order of them being added. + WorkItems list_; + + // The list of executed WorkItems, in the reverse order of them being + // executed. + WorkItems executed_list_; +}; + +#endif // CHROME_INSTALLER_UTIL_WORK_ITEM_LIST_H__ diff --git a/chrome/installer/util/work_item_list_unittest.cc b/chrome/installer/util/work_item_list_unittest.cc new file mode 100644 index 0000000..f381b01 --- /dev/null +++ b/chrome/installer/util/work_item_list_unittest.cc @@ -0,0 +1,192 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include "base/base_paths.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/registry.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/installer/util/work_item.h" +#include "chrome/installer/util/work_item_list.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + wchar_t test_root[] = L"ListList"; + wchar_t data_str[] = L"data_111"; + + class WorkItemListTest : public testing::Test { + protected: + virtual void SetUp() { + // Create a temporary key for testing + RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); + key.DeleteKey(test_root); + ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, test_root, KEY_READ)); + ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, test_root, KEY_READ)); + + // Create a temp directory for test. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + file_util::AppendToPath(&test_dir_, L"WorkItemListTest"); + file_util::Delete(test_dir_, true); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + CreateDirectory(test_dir_.c_str(), NULL); + ASSERT_TRUE(file_util::PathExists(test_dir_)); + + // Create a log file + std::wstring log_file; + ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file)); + ASSERT_TRUE(file_util::PathExists(log_file)); + + logging::InitLogging(log_file.c_str(), + logging::LOG_ONLY_TO_FILE, + logging::LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE); + logging::SetMinLogLevel(logging::LOG_INFO); + } + + virtual void TearDown() { + logging::CloseLogFile(); + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, true)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + // Clean up the temporary key + RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); + ASSERT_TRUE(key.DeleteKey(test_root)); + } + + std::wstring test_dir_; + }; +}; + +// Execute a WorkItem list successfully and then rollback. +TEST_F(WorkItemListTest, ExecutionSuccess) { + scoped_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList()); + scoped_ptr<WorkItem> work_item; + + std::wstring top_dir_to_create(test_dir_); + file_util::AppendToPath(&top_dir_to_create, L"a"); + std::wstring dir_to_create(top_dir_to_create); + file_util::AppendToPath(&dir_to_create, L"b"); + ASSERT_FALSE(file_util::PathExists(dir_to_create)); + + work_item.reset(reinterpret_cast<WorkItem*>( + WorkItem::CreateCreateDirWorkItem(dir_to_create))); + EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release())); + + std::wstring key_to_create(test_root); + file_util::AppendToPath(&key_to_create, L"ExecutionSuccess"); + + work_item.reset(reinterpret_cast<WorkItem*>( + WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create))); + EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release())); + + std::wstring name(L"name"); + std::wstring data(data_str); + work_item.reset(reinterpret_cast<WorkItem*>( + WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, key_to_create, + name, data, false))); + EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release())); + + EXPECT_TRUE(work_item_list->Do()); + + // Verify all WorkItems have been executed. + RegKey key; + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ)); + std::wstring read_out; + EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out)); + EXPECT_EQ(0, read_out.compare(data_str)); + key.Close(); + EXPECT_TRUE(file_util::PathExists(dir_to_create)); + + work_item_list->Rollback(); + + // Verify everything is rolled back. + // The value must have been deleted first in roll back otherwise the key + // can not be deleted. + EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ)); + EXPECT_FALSE(file_util::PathExists(top_dir_to_create)); +} + +// Execute a WorkItem list. Fail in the middle. Rollback what has been done. +TEST_F(WorkItemListTest, ExecutionFailAndRollback) { + scoped_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList()); + scoped_ptr<WorkItem> work_item; + + std::wstring top_dir_to_create(test_dir_); + file_util::AppendToPath(&top_dir_to_create, L"a"); + std::wstring dir_to_create(top_dir_to_create); + file_util::AppendToPath(&dir_to_create, L"b"); + ASSERT_FALSE(file_util::PathExists(dir_to_create)); + + work_item.reset(reinterpret_cast<WorkItem*>( + WorkItem::CreateCreateDirWorkItem(dir_to_create))); + EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release())); + + std::wstring key_to_create(test_root); + file_util::AppendToPath(&key_to_create, L"ExecutionFail"); + + work_item.reset(reinterpret_cast<WorkItem*>( + WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create))); + EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release())); + + std::wstring not_created_key(test_root); + file_util::AppendToPath(¬_created_key, L"NotCreated"); + std::wstring name(L"name"); + std::wstring data(data_str); + work_item.reset(reinterpret_cast<WorkItem*>( + WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, not_created_key, + name, data, false))); + EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release())); + + // This one will not be executed because we will fail early. + work_item.reset(reinterpret_cast<WorkItem*>( + WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, + not_created_key))); + EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release())); + + EXPECT_FALSE(work_item_list->Do()); + + // Verify the first 2 WorkItems have been executed. + RegKey key; + EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ)); + key.Close(); + EXPECT_TRUE(file_util::PathExists(dir_to_create)); + // The last one should not be there. + EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, not_created_key.c_str(), + KEY_READ)); + + work_item_list->Rollback(); + + // Verify everything is rolled back. + EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ)); + EXPECT_FALSE(file_util::PathExists(top_dir_to_create)); +} |