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 | |
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')
82 files changed, 9693 insertions, 0 deletions
diff --git a/chrome/installer/mini_installer/SConscript b/chrome/installer/mini_installer/SConscript new file mode 100644 index 0000000..37cc692 --- /dev/null +++ b/chrome/installer/mini_installer/SConscript @@ -0,0 +1,302 @@ +# 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_res', 'env_test')
+
+
+env = env.Clone()
+env_res = env_res.Clone()
+env_test = env_test.Clone()
+
+
+env_res.Append(
+ CPPPATH = [
+ "$TARGET_ROOT",
+ ".",
+ "#/..",
+ ],
+ RCFLAGS = [
+ ["/l", "0x409"],
+ ],
+)
+
+resources = env_res.RES('mini_installer.rc')
+
+
+env.Prepend(
+ CPPPATH = [
+ '$GTEST_DIR/include',
+ '$GTEST_DIR',
+ '#/..',
+ ],
+ CPPDEFINES = [
+ 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',
+ 'WIN32_LEAN_AND_MEAN',
+ ],
+ CCFLAGS = [
+ '/TP',
+
+ '/Wp64',
+
+ '/wd4503',
+ '/wd4819',
+
+ '/GS-', # because we link with /NODEFAULTLIB
+ ],
+ LINKFLAGS = [
+ '/INCREMENTAL',
+ '/NODEFAULTLIB',
+ '/DEBUG',
+ '/SUBSYSTEM:WINDOWS',
+ '/OPT:NOWIN98',
+ '/ENTRY:"MainEntryPoint"',
+ '/MACHINE:X86',
+ '/FIXED:No',
+
+ '/SAFESEH:NO',
+ '/NXCOMPAT',
+ '/DYNAMICBASE:NO',
+
+ '/PDB:${TARGETS[1]}',
+ '/MAP:${TARGETS[2]}',
+ ],
+)
+
+env['CCFLAGS'].remove('/RTC1')
+
+env.Append(
+ LIBS = [
+ 'shlwapi.lib',
+ 'wininet.lib',
+ 'version.lib',
+ 'msimg32.lib',
+ 'ws2_32.lib',
+ 'usp10.lib',
+ 'psapi.lib',
+ 'kernel32.lib',
+ 'user32.lib',
+ 'gdi32.lib',
+ 'winspool.lib',
+ 'comdlg32.lib',
+ 'advapi32.lib',
+ 'shell32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'uuid.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+ ],
+)
+
+components = [
+ "$VISUAL_STUDIO/VC/crt/src/intel/mt_lib/memset.obj",
+ "$VISUAL_STUDIO/VC/crt/src/intel/mt_lib/P4_memset.obj",
+ "$TARGET_ROOT/chrome_dll.lib",
+]
+
+input_files = [
+ "mini_installer.cc",
+ "pe_resource.cc",
+]
+
+exe = env.Program(['mini_installer',
+ 'mini_installer.pdb',
+ 'mini_installer.map'],
+ components + resources + input_files)
+i = env.Install('$TARGET_ROOT', exe)
+env.Alias('chrome', i)
+
+
+env.AppendENVPath('PATH', r'C:\WINDOWS\system32')
+
+packed = env.Command('$TARGET_ROOT/packed_files.txt',
+ ['$CHROME_DIR/tools/build/win/create_installer_archive.py',
+ '$CHROME_DIR/installer/mini_installer/chrome.release'],
+ ('$PYTHON ${SOURCES[0]}'
+ ' --output_dir=${TARGET.dir}'
+ ' --input_file=${SOURCES[1]}'))
+env.Depends(packed, '$TARGET_ROOT/setup.exe')
+
+
+env_version = env.Clone(
+ VERSION_BAT = File('#/../chrome/tools/build/win/version.bat'),
+ CHROMEDIR = Dir('#/../chrome'),
+ PWD = Dir('.'),
+)
+
+env_version.Command('mini_installer_exe_version.rc',
+ ['mini_installer_exe_version.rc.version',
+ '$CHROMEDIR/VERSION',
+ '$CHROMEDIR/BRANDING'],
+ "$VERSION_BAT $SOURCE $CHROMEDIR $PWD $TARGET")
+
+
+#/Od
+#/I
+#"C:\src\trunk-vs\chrome\..\testing\gtest\include"
+#/I
+#"C:\src\trunk-vs\chrome\..\testing\gtest"
+#/I
+#"C:\src\trunk-vs\chrome\.."
+#/I
+#"C:\src\trunk-vs\chrome\third_party\wtl\include"
+#/I
+#"C:\src\trunk-vs\chrome\..\third_party\platformsdk_vista_6_0\files\Include"
+#/I
+#"C:\src\trunk-vs\chrome\..\third_party\platformsdk_vista_6_0\files\VC\INCLUDE"
+#/I
+#"C:\Program
+#Files\Microsoft
+#Visual
+#Studio
+#8\\VC\atlmfc\include"
+#/D
+#"UNIT_TEST"
+#/D
+#"_DEBUG"
+#/D
+#"_WIN32_WINNT=0x0600"
+#/D
+#"WINVER=0x0600"
+#/D
+#"WIN32"
+#/D
+#"_WINDOWS"
+#/D
+#"_HAS_EXCEPTIONS=0"
+#/D
+#"NOMINMAX"
+#/D
+#"_CRT_RAND_S"
+#/D
+#"CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"
+#/D
+#"WIN32_LEAN_AND_MEAN"
+#/D
+#"_UNICODE"
+#/D
+#"UNICODE"
+#/FD
+#/RTC1
+#/MTd
+#/Gy
+#/GR-
+#/Fo"C:\src\trunk-vs\chrome\Debug\obj\installer_unittests\\"
+#/Fd"C:\src\trunk-vs\chrome\Debug\obj\installer_unittests\vc80.pdb"
+#/W3
+#/WX
+#/c
+#/Wp64
+#/Zi
+#/TP
+#/wd4503
+#/wd4819
+
+env_test.Prepend(
+ CPPDEFINES = [
+ 'UNIT_TEST',
+ 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',
+ 'WIN32_LEAN_AND_MEAN',
+ ],
+ CPPPATH = [
+ '$GTEST_DIR/include',
+ '$GTEST_DIR',
+ '#/..',
+ ],
+ LINKFLAGS = [
+ '/INCREMENTAL',
+ '/DEBUG',
+
+ '/DELAYLOAD:"dwmapi.dll"',
+ '/DELAYLOAD:"uxtheme.dll"',
+
+ '/MACHINE:X86',
+ '/FIXED:No',
+
+ '/safeseh',
+ '/dynamicbase',
+ '/ignore:4199',
+ '/nxcompat',
+ ],
+ LIBS = [
+ 'shlwapi.lib',
+ 'rpcrt4.lib',
+ 'oleacc.lib',
+ 'comsupp.lib',
+
+ 'wininet.lib',
+ 'version.lib',
+ 'msimg32.lib',
+ 'ws2_32.lib',
+ 'usp10.lib',
+ 'psapi.lib',
+ 'kernel32.lib',
+ 'user32.lib',
+ 'gdi32.lib',
+ 'winspool.lib',
+ 'comdlg32.lib',
+ 'advapi32.lib',
+ 'shell32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'uuid.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+
+ 'DelayImp.lib',
+ ],
+)
+
+input_files = [
+ '../setup/setup_constants$OBJSUFFIX',
+ '../util/copy_tree_work_item_unittest.cc',
+ '../util/create_dir_work_item_unittest.cc',
+ '../util/create_reg_key_work_item_unittest.cc',
+ '../util/delete_tree_work_item_unittest.cc',
+ '../util/helper_unittest.cc',
+ '../util/install_util_unittest.cc',
+ '../util/run_all_unittests.cc',
+ '../util/set_reg_value_work_item_unittest.cc',
+ '../util/work_item_list_unittest.cc',
+]
+
+libs = [
+ '../util/util.lib',
+ '$TESTING_DIR/gtest.lib',
+ '$ICU38_DIR/icuuc.lib',
+ '$CHROME_DIR/common/common.lib',
+ '$BASE_DIR/base.lib',
+]
+
+exe = env_test.Program(['installer_unittests',
+ 'installer_unittests.pdb'],
+ input_files + libs)
+i = env_test.Install('$TARGET_ROOT', exe)
+
+env.Alias('chrome', i)
diff --git a/chrome/installer/mini_installer/chrome.release b/chrome/installer/mini_installer/chrome.release new file mode 100644 index 0000000..e9fc438 --- /dev/null +++ b/chrome/installer/mini_installer/chrome.release @@ -0,0 +1,40 @@ +# 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. + +[FILES] +chrome.exe: %(ChromeDir)s\ +wow_helper.exe: %(ChromeDir)s\ +Dictionaries\en-US.bdic: %(ChromeDir)s\Dictionaries +rlz.dll: %(VersionDir)s\ +chrome.dll: %(VersionDir)s\ +icudt38.dll: %(VersionDir)s\ +Themes\default.dll: %(VersionDir)s\Themes +locales\*.dll: %(VersionDir)s\Locales +Resources\Inspector\*.*: %(VersionDir)s\Resources\Inspector +Resources\Inspector\Images\*.*: %(VersionDir)s\Resources\Inspector\Images diff --git a/chrome/installer/mini_installer/mini_installer.cc b/chrome/installer/mini_installer/mini_installer.cc new file mode 100644 index 0000000..7c1650e --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer.cc @@ -0,0 +1,392 @@ +// 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. + +// mini_installer.exe is the first exe that is run when chrome is being +// installed or upgraded. It is designed to be extremely small (~5KB with no +// extra resources linked) and it has two main jobs: +// 1) unpack the resources (possibly decompressing some) +// 2) run the real installer (setup.exe) with appropiate flags. +// +// In order to be really small we don't link against the CRT and we define the +// following compiler/linker flags: +// EnableIntrinsicFunctions="true" compiler: /Oi +// BasicRuntimeChecks="0" +// BufferSecurityCheck="false" compiler: /GS- +// EntryPointSymbol="MainEntryPoint" linker: /ENTRY +// IgnoreAllDefaultLibraries="true" linker: /NODEFAULTLIB +// OptimizeForWindows98="1" liker: /OPT:NOWIN98 +// linker: /SAFESEH:NO +// Also some built-in code that the compiler relies on is not defined so we +// are forced to manually link against it. It comes in the form of two +// object files that exist in $(VCInstallDir)\crt\src which are memset.obj and +// P4_memset.obj. These two object files rely on the existence of a static +// variable named __sse2_available which indicates the presence of intel sse2 +// extensions. We define it to false which causes a slower but safe code for +// memcpy and memset intrinsics. + +// having the linker merge the sections is saving us ~500 bytes. +#pragma comment(linker, "/MERGE:.rdata=.text") + +#include <windows.h> +#include <shlwapi.h> +#include <stdlib.h> + +#include "chrome/installer/mini_installer/mini_installer.h" +#include "chrome/installer/mini_installer/pe_resource.h" + +// Required linker symbol. See remarks above. +extern "C" unsigned int __sse2_available = 0; + +namespace mini_installer { + +// This structure passes data back and forth for the processing +// of resource callbacks. +struct Context { + // We really have a single string that is used for all operations. + // We keep two pointers to it as follows: + // [uncompress command]' '[path to exe]\[name of file] + // | | + // Context->full_path Context->name + wchar_t* full_path; // input to the call back method + wchar_t* name; // input/output from the call back method (contains file name) + size_t max_name_size; // input (contains the size of buffer "name") +}; + + +// Takes the path to file and returns a pointer to the filename component. For +// exmaple for input of c:\full\path\to\file.ext it returns pointer to file.ext. +// It also checks that there is an extension (of length 3) in file name and +// the size of path is at least 7. It returns NULL if extension or path +// separator is not found. +wchar_t* GetNameFromPathExt(wchar_t* path, size_t size) { + const int kMinPath = 7; + const int kExtSize = 4; + if ((size < kMinPath) || (path[size - kExtSize] != L'.')) { + return NULL; + } + wchar_t* current = &path[size - kExtSize]; + while (L'\\' != *current) { + --current; + if (current == path) { + return NULL; + } + } + return (current + 1); +} + + +// Simple replacement for CRT string copy method that does not overflow. +// Returns true if the source was copied successfully otherwise returns false. +// Parameter src is assumed to be NULL terminated and the NULL character is +// copied over to string dest. +bool SafeStrCopy(wchar_t* dest, const wchar_t* src, size_t dest_size) { + for (size_t length = 0; length < dest_size; ++dest, ++src, ++length) { + *dest = *src; + if (L'\0' == *src) { + return true; + } + } + return false; +} + + +// Helper function to read a value from registry. Returns true if value +// is read successfully and stored in parameter value. Returns false otherwise. +bool ReadValueFromRegistry(HKEY root_key, const wchar_t *sub_key, + const wchar_t *value_name, wchar_t *value, + size_t size) { + HKEY key; + + if ((::RegOpenKeyEx(root_key, sub_key, NULL, + KEY_READ, &key) == ERROR_SUCCESS) && + (::RegQueryValueEx(key, value_name, NULL, NULL, + reinterpret_cast<LPBYTE>(value), + reinterpret_cast<LPDWORD>(&size)) == ERROR_SUCCESS)) { + ::RegCloseKey(key); + return true; + } + return false; +} + + +// Gets the setup.exe path from Registry by looking the value of Uninstall +// string, strips the arguments for uninstall and returns only the full path +// to setup.exe. +bool GetSetupExePathFromRegistry(wchar_t *path, size_t size) { + if (!ReadValueFromRegistry(HKEY_CURRENT_USER, kUninstallRegistryKey, + kUninstallRegistryValueName, path, size)) { + if (!ReadValueFromRegistry(HKEY_LOCAL_MACHINE, kUninstallRegistryKey, + kUninstallRegistryValueName, path, size)) { + return false; + } + } + wchar_t *tmp = StrStr(path, L" --"); + if (tmp) { + *tmp = L'\0'; + } else { + return false; + } + + return true; +} + + +// Calls CreateProcess with good default parameters and waits for the process +// to terminate returning the process exit code. +bool RunProcessAndWait(const wchar_t* exe_path, wchar_t* cmdline, + int* exit_code) { + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi = {0}; + if (!::CreateProcessW(exe_path, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, + NULL, NULL, &si, &pi)) { + return false; + } + DWORD wr = ::WaitForSingleObject(pi.hProcess, INFINITE); + if (WAIT_OBJECT_0 != wr) { + return false; + } + + bool ret = true; + if (exit_code) { + if (!::GetExitCodeProcess(pi.hProcess, + reinterpret_cast<DWORD*>(exit_code))) { + ret = false; + } + } + ::CloseHandle(pi.hProcess); + ::CloseHandle(pi.hThread); + return ret; +} + + +// Windows defined callback used in the EnumResourceNamesW call. For each +// matching resource found, the callback is invoked and at this point we write +// it to disk and possibly uncompress it. Resources of type BL +// are assumed to be LZ compressed and are uncompressed using 'expand.exe'. +BOOL CALLBACK OnResourceFound(HMODULE module, const wchar_t* type, + wchar_t* name, LONG_PTR context) { + if (NULL == context) { + return FALSE; + } + Context* ctx = reinterpret_cast<Context*>(context); + + PEResource resource(name, type, module); + if ((!resource.IsValid()) || + (resource.Size() < 1 ) || + (resource.Size() > kMaxResourceSize)) { + return FALSE; + } + + // Note that the copy operation actually replaces the file name part of + // ctx->full_path + if ((!SafeStrCopy(ctx->name, name, ctx->max_name_size)) || + (!resource.WriteToDisk(ctx->full_path))) { + return FALSE; + } + + // If this is LZ compressed resource, uncompress it using the existing + // program in the system32 folder named 'expand.exe'. + if (0 == ::lstrcmpiW(type, kLZCResourceType)) { + wchar_t expand_cmd[MAX_PATH * 2] = UNCOMPRESS_CMD; + ::lstrcatW(expand_cmd, L"\""); + ::lstrcatW(expand_cmd, ctx->full_path); + ::lstrcatW(expand_cmd, L"\""); + if (!RunProcessAndWait(NULL, expand_cmd, NULL)) { + // Somehow we failed to uncompress the file. Exit now and leave the file + // behind for postmortem analysis. + return FALSE; + } + // Uncompression was successful, delete the source but it is not critical + // if that fails. + ::DeleteFileW(ctx->full_path); + } + return TRUE; +} + + +// Finds and writes to disk all resources of various types. Returns false +// if there is a problem in writing any resource to disk. +bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path, + bool* unpacked_setup, wchar_t* archive_name) { + *unpacked_setup = false; + + // Prepare the input to OnResourceFound method that needs a location where + // it will write all the resources. + wchar_t module_path[MAX_PATH]; + if (!SafeStrCopy(module_path, base_path, _countof(module_path))) { + return false; + } + size_t length = ::lstrlen(module_path); + wchar_t* name = (wchar_t *) module_path + length; + Context context = {module_path, name, MAX_PATH - length}; + + // Get the resources of type 'B7' + if (!::EnumResourceNamesW(module, kLZMAResourceType, OnResourceFound, + LONG_PTR(&context))) { + // We need a compressed archive to do the installation. So if there + // is a problem in fetching B7 resource, just return error. + return false; + } else { + ::lstrcpy(archive_name, name); + } + + // Get the resources of type 'BL'. setup.exe can be sent as 'BL' or 'BN'. + // So if we get 'resource not found' error just ignore it. + if (!::EnumResourceNamesW(module, kLZCResourceType, OnResourceFound, + LONG_PTR(&context))) { + if (::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND) { + return false; + } + } else if (0 == ::lstrcmpiW(name, kSetupLZName)) { + *unpacked_setup = true; + } + + if (!::EnumResourceNamesW(module, kBinResourceType, OnResourceFound, + LONG_PTR(&context))) { + if (::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND) { + return false; + } + } else if (0 == ::lstrcmpiW(name, kSetupName)) { + *unpacked_setup = true; + } + + return true; +} + + +// Executes setup.exe, waits for it to finish and returns the exit code. +bool RunSetup(bool have_upacked_setup, const wchar_t* base_path, + const wchar_t* archive_name, int* exit_code) { + wchar_t cmd_line[MAX_PATH * 2]; + wchar_t cmd_args[MAX_PATH * 2]; + + if (!SafeStrCopy(cmd_args, L" --install-archive=\"", _countof(cmd_args)) || + !::lstrcat(cmd_args, base_path) || + !::lstrcat(cmd_args, archive_name) || + !::lstrcat(cmd_args, L"\"")) { + return false; + } + + if (have_upacked_setup) { + if (!SafeStrCopy(cmd_line, L"\"", _countof(cmd_line)) || + !::lstrcat(cmd_line, base_path) || + !::lstrcat(cmd_line, kSetupName) || + !::lstrcat(cmd_line, L"\"")) { + return false; + } + } else { + if (!GetSetupExePathFromRegistry(cmd_line, sizeof(cmd_line))) { + return false; + } + } + + return (::lstrcat(cmd_line, cmd_args) && + RunProcessAndWait(NULL, cmd_line, exit_code)); +} + + +void DeleteExtractedFiles(const wchar_t* base_path, + const wchar_t* archive_name) { + wchar_t file_path[MAX_PATH]; + // Delete setup.exe. + SafeStrCopy(file_path, base_path, MAX_PATH); + ::lstrcat(file_path, kSetupName); + ::DeleteFile(file_path); + // Delete chrome archive file. + SafeStrCopy(file_path, base_path, MAX_PATH); + ::lstrcat(file_path, archive_name); + ::DeleteFile(file_path); + // Delete the temp dir (if it is empty, otherwise fail). + ::RemoveDirectory(base_path); +} + +// Creates and returns a temporary directory that can be used to extract +// mini_installer payload. +bool GetWorkDir(HMODULE module, wchar_t* work_dir) { + wchar_t base_path[MAX_PATH]; + DWORD len = ::GetTempPath(MAX_PATH, base_path); + if (len >= MAX_PATH || len <= 0) { + // Problem in getting TEMP path so just use current directory as base path + len = ::GetModuleFileNameW(module, base_path, MAX_PATH); + if (len >= MAX_PATH || len <= 0) + return false; // Can't even get current directory? Return with error. + wchar_t* name = GetNameFromPathExt(base_path, len); + *name = L'\0'; + } + + wchar_t temp_name[MAX_PATH + 1]; + if (!GetTempFileName(base_path, L"CR_", 0, temp_name)) + return false; // Didn't get any temp name to use. Return error. + len = GetLongPathName(temp_name, work_dir, MAX_PATH); + if (len > MAX_PATH + 1 || len == 0) + return false; // Couldn't get full path to temp dir. Return error. + + // GetTempFileName creates the file as well so delete it before creating + // the directory in its place. + if (!::DeleteFile(work_dir) || !::CreateDirectory(work_dir, NULL)) + return false; // What's the use of temp dir if we can not create it? + ::lstrcat(work_dir, L"\\"); + return true; +} + +int WMain(HMODULE module) { + // First get a path where we can extract payload + wchar_t base_path[MAX_PATH]; + if (!GetWorkDir(module, base_path)) + return 1; + + wchar_t archive_name[MAX_PATH]; + bool have_upacked_setup; + if (!UnpackBinaryResources(module, base_path, + &have_upacked_setup, archive_name)) { + return 1; + } + + int setup_exit_code = 0; + if (!RunSetup(have_upacked_setup, base_path, + archive_name, &setup_exit_code)) { + return 2; + } + + wchar_t value[4]; + if ((!ReadValueFromRegistry(HKEY_CURRENT_USER, kCleanupRegistryKey, + kCleanupRegistryValueName, value, 4)) || + (value[0] != L'0')) { + DeleteExtractedFiles(base_path, archive_name); + } + + return setup_exit_code; +} +} // namespace mini_installer + + +int MainEntryPoint() { + int result = mini_installer::WMain(::GetModuleHandle(NULL)); + ::ExitProcess(result); +} diff --git a/chrome/installer/mini_installer/mini_installer.exe.manifest b/chrome/installer/mini_installer/mini_installer.exe.manifest new file mode 100644 index 0000000..28469a3 --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer.exe.manifest @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <ms_asmv2:trustInfo xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
+ <ms_asmv2:security>
+ <ms_asmv2:requestedPrivileges>
+ <ms_asmv2:requestedExecutionLevel level="asInvoker">
+ </ms_asmv2:requestedExecutionLevel>
+ </ms_asmv2:requestedPrivileges>
+ </ms_asmv2:security>
+ </ms_asmv2:trustInfo>
+</assembly>
diff --git a/chrome/installer/mini_installer/mini_installer.h b/chrome/installer/mini_installer/mini_installer.h new file mode 100644 index 0000000..785749e --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer.h @@ -0,0 +1,68 @@ +// 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_MINI_INSTALLER__ +#define CHROME_INSTALLER_MINI_INSTALLER__ + +// The windows command line to uncompress a LZ compressed file. It is a define +// because we need the string to be writable. We don't need the full path +// since it is located in windows\system32 and is available since windows2k. +#define UNCOMPRESS_CMD L"expand.exe -r " + +namespace mini_installer { + +// The installer filename. The installer can be compressed or uncompressed. +const wchar_t kSetupName[] = L"setup.exe"; +const wchar_t kSetupLZName[] = L"setup.ex_"; + +// The resource types that would be unpacked from the mini installer. +// 'BN' is uncompressed binary and 'BL' is LZ compressed binary. +const wchar_t kBinResourceType[] = L"BN"; +const wchar_t kLZCResourceType[] = L"BL"; +const wchar_t kLZMAResourceType[] = L"B7"; + +// Uninstall registry location +const wchar_t kUninstallRegistryKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Chrome"; +const wchar_t kUninstallRegistryValueName[] = L"UninstallString"; + +// Uninstall registry key that lets user tell Chrome installer not to delete +// extracted files. +const wchar_t kCleanupRegistryKey[] = L"Software\\Google"; +const wchar_t kCleanupRegistryValueName[] = L"ChromeInstallerCleanup"; + +// One gigabyte is the biggest resource size that it can handle. +const int kMaxResourceSize = 1024*1024*1024; + +// This is the file that contains the list of files to be linked in the +// executable. This file is updated by the installer generator tool chain. +const wchar_t kManifestFilename[] = L"packed_files.txt"; + +} // namespace mini_installer + +#endif // CHROME_INSTALLER_MINI_INSTALLER__ diff --git a/chrome/installer/mini_installer/mini_installer.ico b/chrome/installer/mini_installer/mini_installer.ico Binary files differnew file mode 100644 index 0000000..6612f2d --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer.ico diff --git a/chrome/installer/mini_installer/mini_installer.rc b/chrome/installer/mini_installer/mini_installer.rc new file mode 100644 index 0000000..420dea9 --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer.rc @@ -0,0 +1,76 @@ +// Microsoft Visual C++ generated resource script. +// +#include "mini_installer_resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MINI_INSTALLER ICON "mini_installer.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "mini_installer_resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOL\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""mini_installer_exe_version.rc""\r\0" +END + +#endif // APSTUDIO_INVOKED + +// This file lists the resources that are going to be packed with the exe. +#include "packed_files.txt" + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "mini_installer_exe_version.rc" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/chrome/installer/mini_installer/mini_installer.vcproj b/chrome/installer/mini_installer/mini_installer.vcproj new file mode 100644 index 0000000..96cfe0a --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer.vcproj @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="mini_installer" + ProjectGUID="{24A5AC7C-280B-4899-9153-6BA570A081E7}" + RootNamespace="mini_installer" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + <ToolFile + RelativePath="..\..\tools\build\win\release.rules" + /> + <ToolFile + RelativePath="..\..\tools\build\win\version.rules" + /> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="1" + InheritedPropertySheets=".\mini_installer_debug.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops" + > + <Tool + Name="create installer archive" + LastChromeInstaller="$(LAST_CHROME_INSTALLER)" + LastChromeVersion="$(LAST_CHROME_VERSION)" + RebuildArchive="$(REBUILD_CHROME_ARCHIVE)" + /> + <Tool + Name="Version" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCLinkerTool" + /> + <Tool + Name="VCManifestTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="1" + InheritedPropertySheets=".\mini_installer_release.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops" + > + <Tool + Name="create installer archive" + LastChromeInstaller="$(LAST_CHROME_INSTALLER)" + LastChromeVersion="$(LAST_CHROME_VERSION)" + RebuildArchive="$(REBUILD_CHROME_ARCHIVE)" + /> + <Tool + Name="Version" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCLinkerTool" + /> + <Tool + Name="VCManifestTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="resources" + > + <File + RelativePath=".\mini_installer.ico" + > + </File> + <File + RelativePath=".\mini_installer.rc" + > + </File> + <File + RelativePath=".\mini_installer_exe_version.rc.version" + > + </File> + <File + RelativePath=".\mini_installer_resource.h" + > + </File> + </Filter> + <File + RelativePath=".\chrome.release" + > + </File> + <File + RelativePath=".\mini_installer.cc" + > + </File> + <File + RelativePath=".\mini_installer.h" + > + </File> + <File + RelativePath=".\pe_resource.cc" + > + </File> + <File + RelativePath=".\pe_resource.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/installer/mini_installer/mini_installer.vsprops b/chrome/installer/mini_installer/mini_installer.vsprops new file mode 100644 index 0000000..c3920b0 --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer.vsprops @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="mini_installer" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops" + > + <Tool + Name="VCCLCompilerTool" + EnableIntrinsicFunctions="true" + BasicRuntimeChecks="0" + BufferSecurityCheck="false" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies=""$(VCInstallDir)crt\src\intel\mt_lib\memset.obj" "$(VCInstallDir)crt\src\intel\mt_lib\P4_memset.obj" shlwapi.lib" + IgnoreAllDefaultLibraries="true" + GenerateMapFile="true" + SubSystem="2" + OptimizeForWindows98="1" + EntryPointSymbol="MainEntryPoint" + /> + <Tool + Name="VCResourceCompilerTool" + AdditionalIncludeDirectories="$(OutDir);$(IntDir)" + /> + <Tool + Name="VCManifestTool" + AdditionalManifestFiles="$(SolutionDir)installer\mini_installer\mini_installer.exe.manifest" + /> +</VisualStudioPropertySheet> diff --git a/chrome/installer/mini_installer/mini_installer_debug.vsprops b/chrome/installer/mini_installer/mini_installer_debug.vsprops new file mode 100644 index 0000000..9b43a5a --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer_debug.vsprops @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="mini_installer_debug" + InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\mini_installer.vsprops" + > + <Tool + Name="VCLinkerTool" + AdditionalOptions="$(NoInherit) /SAFESEH:NO /NXCOMPAT /DYNAMICBASE:NO /FIXED" + DelayLoadDLLs="$(NoInherit)" + /> +</VisualStudioPropertySheet> diff --git a/chrome/installer/mini_installer/mini_installer_exe_version.rc.version b/chrome/installer/mini_installer/mini_installer_exe_version.rc.version new file mode 100644 index 0000000..3b024d1 --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer_exe_version.rc.version @@ -0,0 +1,40 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @MAJOR@,@MINOR@,@BUILD@,@PATCH@ + PRODUCTVERSION @MAJOR@,@MINOR@,@BUILD@,@PATCH@ + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "@COMPANY_FULLNAME@" + VALUE "FileDescription", "@PRODUCT_FULLNAME@" + VALUE "FileVersion", "@MAJOR@.@MINOR@.@BUILD@.@PATCH@" + VALUE "InternalName", "mini_installer" + VALUE "LegalCopyright", "@COPYRIGHT@" + VALUE "ProductName", "@PRODUCT_FULLNAME@" + VALUE "ProductVersion", "@MAJOR@.@MINOR@.@BUILD@.@PATCH@" + VALUE "CompanyShortName", "@COMPANY_SHORTNAME@" + VALUE "ProductShortName", "@PRODUCT_SHORTNAME@" + VALUE "LastChange", "@LASTCHANGE@" + VALUE "Official Build", "@OFFICIAL_BUILD@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/chrome/installer/mini_installer/mini_installer_release.vsprops b/chrome/installer/mini_installer/mini_installer_release.vsprops new file mode 100644 index 0000000..339f174 --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer_release.vsprops @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="mini_installer_release" + InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\mini_installer.vsprops" + > + <Tool + Name="VCLinkerTool" + AdditionalOptions="$(NoInherit) /SAFESEH:NO /NXCOMPAT /DYNAMICBASE:NO /FIXED" + DelayLoadDLLs="$(NoInherit)" + /> +</VisualStudioPropertySheet> diff --git a/chrome/installer/mini_installer/mini_installer_resource.h b/chrome/installer/mini_installer/mini_installer_resource.h new file mode 100644 index 0000000..ca01e39 --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer_resource.h @@ -0,0 +1,18 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by mini_installer.rc +// +#define IDI_MINI_INSTALLER 107 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/chrome/installer/mini_installer/pe_resource.cc b/chrome/installer/mini_installer/pe_resource.cc new file mode 100644 index 0000000..6084fd3 --- /dev/null +++ b/chrome/installer/mini_installer/pe_resource.cc @@ -0,0 +1,73 @@ +// 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/mini_installer/pe_resource.h" + +PEResource::PEResource(HRSRC resource, HMODULE module) + : resource_(resource), module_(module) { +} + +PEResource::PEResource(const wchar_t* name, const wchar_t* type, HMODULE module) + : resource_(NULL), module_(module) { + resource_ = ::FindResourceW(module, name, type); +} + +bool PEResource::IsValid() { + return (NULL != resource_); +} + +size_t PEResource::Size() { + return ::SizeofResource(module_, resource_); +} + +bool PEResource::WriteToDisk(const wchar_t* full_path) { + // Resource handles are not real HGLOBALs so do not attempt to close them. + // Windows frees them whenever there is memory pressure. + HGLOBAL data_handle = ::LoadResource(module_, resource_); + if (NULL == data_handle) { + return false; + } + void* data = ::LockResource(data_handle); + if (NULL == data) { + return false; + } + size_t resource_size = Size(); + HANDLE out_file = ::CreateFileW(full_path, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == out_file) { + return false; + } + DWORD written = 0; + if (!::WriteFile(out_file, data, static_cast<DWORD>(resource_size), + &written, NULL)) { + ::CloseHandle(out_file); + return false; + } + return ::CloseHandle(out_file) ? true : false; +} diff --git a/chrome/installer/mini_installer/pe_resource.h b/chrome/installer/mini_installer/pe_resource.h new file mode 100644 index 0000000..10c892c --- /dev/null +++ b/chrome/installer/mini_installer/pe_resource.h @@ -0,0 +1,66 @@ +// 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_MINI_INSTALLER_PE_RESOURCE__ +#define CHROME_INSTALLER_MINI_INSTALLER_PE_RESOURCE__ + +#include <windows.h> + +// This class models a windows PE resource. It does not pretend to be a full +// API wrapper and it is just concerned with loading it to memory and writing +// it to disk. Each resource is unique only in the context of a loaded module, +// that is why you need to specify one on each constructor. +class PEResource { + public: + // This ctor takes the handle to the resource and the module where it was + // found. Ownership of the resource is transfered to this object. + PEResource(HRSRC resource, HMODULE module); + + // This ctor takes the resource name, the resource type and the module where + // to look for the resource. If the resource is found IsValid() returns true. + PEResource(const wchar_t* name, const wchar_t* type, HMODULE module); + + // Returns true if the resource is valid. + bool IsValid(); + + // Returns the size in bytes of the resource. Returns zero if the resource is + // not valid. + size_t Size(); + + // Creates a file in 'path' with a copy of the resource. If the resource can + // not be loaded into memory or if it cannot be written to disk it returns + // false. + bool WriteToDisk(const wchar_t* path); + + private: + HRSRC resource_; + HMODULE module_; +}; + +#endif // CHROME_INSTALLER_MINI_INSTALLER_PE_RESOURCE__ diff --git a/chrome/installer/setup/SConscript b/chrome/installer/setup/SConscript new file mode 100644 index 0000000..b26790f --- /dev/null +++ b/chrome/installer/setup/SConscript @@ -0,0 +1,153 @@ +# 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_res')
+
+env = env.Clone()
+env_res = env_res.Clone()
+
+
+env_res.Append(
+ CPPPATH = [
+ "$TARGET_ROOT",
+ ".",
+ "#/..",
+ ],
+ RCFLAGS = [
+ ["/l", "0x409"],
+ ],
+)
+
+
+resources = [
+ env_res.RES('setup.rc'),
+ env_res.RES('../util/setup_strings.rc'),
+]
+
+
+env.Prepend(
+ CPPPATH = [
+ '../util',
+ '$TARGET_ROOT',
+ '.',
+ '#/..',
+ ],
+ LINKFLAGS = [
+ '/INCREMENTAL',
+ '/DEBUG',
+
+ '/DELAYLOAD:"dwmapi.dll"',
+ '/DELAYLOAD:"uxtheme.dll"',
+
+ '/OPT:NOWIN98',
+ '/SUBSYSTEM:WINDOWS',
+ '/MACHINE:X86',
+ '/FIXED:No',
+
+ '/safeseh',
+ '/dynamicbase',
+ '/ignore:4199',
+ '/nxcompat',
+
+ '/PDB:${TARGETS[1]}',
+ '/MAP:${TARGETS[2]}',
+ ],
+ LIBS = [
+ 'shlwapi.lib',
+
+ 'msi.lib',
+
+ 'wininet.lib',
+ 'version.lib',
+ 'msimg32.lib',
+ 'ws2_32.lib',
+ 'usp10.lib',
+ 'psapi.lib',
+ 'kernel32.lib',
+ 'user32.lib',
+ 'gdi32.lib',
+ 'winspool.lib',
+ 'comdlg32.lib',
+ 'advapi32.lib',
+ 'shell32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'uuid.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+
+ 'DelayImp.lib',
+ ],
+)
+
+input_files = [
+ 'install.cc',
+ 'main.cc',
+ 'setup.cc',
+ 'setup_constants.cc',
+ 'uninstall.cc',
+]
+
+libs = [
+ '../util/util.lib',
+ '$BSPATCH_DIR/bspatch.lib',
+ '$LZMA_SDK_DIR/lzma_sdk.lib',
+ '$ICU38_DIR/icuuc.lib',
+ '$CHROME_DIR/common/common.lib',
+ '$BASE_DIR/base.lib',
+]
+
+exe = env.Program(['setup',
+ 'setup.pdb',
+ 'setup.map'],
+ resources + input_files + libs)
+i = env.Install('$TARGET_ROOT', exe)
+
+env.Alias('chrome', i)
+
+
+
+env_version = env.Clone(
+ VERSION_BAT = File('#/../chrome/tools/build/win/version.bat'),
+ CHROMEDIR = Dir('#/../chrome'),
+ PWD = Dir('.'),
+)
+
+import os
+env_version['ENV']['PROGRAMFILES'] = os.environ['PROGRAMFILES']
+env_version['ENV']['SystemDrive'] = os.environ['SystemDrive']
+env_version['ENV']['USERPROFILE'] = os.environ['USERPROFILE']
+env_version['ENV']['PATH'] = os.environ['PATH']
+
+setup_exe_version_rc = env_version.Command(
+ 'setup_exe_version.rc',
+ ['setup_exe_version.rc.version',
+ '$CHROMEDIR/VERSION',
+ '$CHROMEDIR/BRANDING'],
+ '$VERSION_BAT $SOURCE $CHROMEDIR $PWD $TARGET')
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc new file mode 100644 index 0000000..5cd0f22 --- /dev/null +++ b/chrome/installer/setup/install.cc @@ -0,0 +1,269 @@ +// 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 <time.h> + +#include "base/file_util.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/installer/setup/setup.h" +#include "chrome/installer/setup/setup_constants.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/install_util.h" +#include "chrome/installer/util/logging_installer.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/set_reg_value_work_item.h" +#include "chrome/installer/util/shell_util.h" +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/version.h" +#include "chrome/installer/util/work_item_list.h" + +namespace { +std::wstring AppendPath(const std::wstring parent_path, + const std::wstring path) { + std::wstring new_path(parent_path); + file_util::AppendToPath(&new_path, path); + return new_path; +} + + +// This method adds work items to create (or update) Chrome uninstall entry in +// Control Panel->Add/Remove Programs list. +void AddUninstallShortcutWorkItems(HKEY reg_root, + const std::wstring& exe_path, + const std::wstring& install_path, + const std::wstring& new_version, + WorkItemList* install_list) { + std::wstring uninstall_cmd(L"\""); + uninstall_cmd.append(installer::GetInstallerPathUnderChrome(install_path, + new_version)); + file_util::AppendToPath(&uninstall_cmd, + file_util::GetFilenameFromPath(exe_path)); + uninstall_cmd.append(L"\" --"); + uninstall_cmd.append(installer_util::switches::kUninstall); + + // Create DisplayName, UninstallString and InstallLocation keys + install_list->AddCreateRegKeyWorkItem(reg_root, + installer_util::kUninstallRegPath); + install_list->AddSetRegValueWorkItem( + reg_root, + installer_util::kUninstallRegPath, + installer_util::kUninstallDisplayNameField, + installer_util::kChrome, true); + install_list->AddSetRegValueWorkItem(reg_root, + installer_util::kUninstallRegPath, + installer_util::kUninstallStringField, + uninstall_cmd, true); + install_list->AddSetRegValueWorkItem(reg_root, + installer_util::kUninstallRegPath, + L"InstallLocation", install_path, true); + + // DisplayIcon, NoModify and NoRepair + std::wstring chrome_icon = AppendPath(install_path, + installer_util::kChromeExe); + ShellUtil::GetChromeIcon(chrome_icon); + install_list->AddSetRegValueWorkItem(reg_root, + installer_util::kUninstallRegPath, + L"DisplayIcon", chrome_icon, true); + install_list->AddSetRegValueWorkItem(reg_root, + installer_util::kUninstallRegPath, + L"NoModify", 1, true); + install_list->AddSetRegValueWorkItem(reg_root, + installer_util::kUninstallRegPath, + L"NoRepair", 1, true); + + install_list->AddSetRegValueWorkItem(reg_root, + installer_util::kUninstallRegPath, + L"Publisher", + installer_util::kPublisherName, true); + install_list->AddSetRegValueWorkItem(reg_root, + installer_util::kUninstallRegPath, + L"Version", new_version.c_str(), true); + install_list->AddSetRegValueWorkItem(reg_root, + installer_util::kUninstallRegPath, + L"DisplayVersion", + new_version.c_str(), true); + time_t rawtime = time(NULL); + struct tm timeinfo = {0}; + localtime_s(&timeinfo, &rawtime); + wchar_t buffer[9]; + if (wcsftime(buffer, 9, L"%Y%m%d", &timeinfo) == 8) { + install_list->AddSetRegValueWorkItem(reg_root, + installer_util::kUninstallRegPath, + L"InstallDate", + buffer, false); + } +} + +void AddInstallerCopyTasks(const std::wstring& exe_path, + const std::wstring& archive_path, + const std::wstring& temp_path, + const std::wstring& install_path, + const std::wstring& new_version, + WorkItemList* install_list) { + std::wstring installer_dir(installer::GetInstallerPathUnderChrome( + install_path, new_version)); + install_list->AddCreateDirWorkItem(installer_dir); + + std::wstring exe_dst(installer_dir); + std::wstring archive_dst(installer_dir); + file_util::AppendToPath(&exe_dst, + file_util::GetFilenameFromPath(exe_path)); + file_util::AppendToPath(&archive_dst, + file_util::GetFilenameFromPath(archive_path)); + + install_list->AddCopyTreeWorkItem(exe_path, exe_dst, temp_path, + WorkItem::ALWAYS); + install_list->AddCopyTreeWorkItem(archive_path, archive_dst, temp_path, + WorkItem::ALWAYS); +} + + +// This method tells if we are running on 64 bit platform so that we can copy +// one extra exe. If the API call to determine 64 bit fails, we play it safe +// and return true anyway so that the executable can be copied. +bool Is64bit() { + typedef BOOL (WINAPI *WOW_FUNC)(HANDLE, PBOOL); + BOOL is64 = FALSE; + + HANDLE handle = GetCurrentProcess(); + HMODULE module = GetModuleHandle(L"kernel32.dll"); + WOW_FUNC p = reinterpret_cast<WOW_FUNC>(GetProcAddress(module, + "IsWow64Process")); + if ((p != NULL) && (!(p)(handle, &is64) || (is64 != FALSE))) { + return true; + } + + return false; +} + +} + +bool installer::InstallNewVersion(const std::wstring& exe_path, + const std::wstring& archive_path, + const std::wstring& src_path, + const std::wstring& install_path, + const std::wstring& temp_dir, + const HKEY reg_root, + const Version& new_version) { + + if (reg_root != HKEY_LOCAL_MACHINE && reg_root != HKEY_CURRENT_USER) + return false; + + scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); + // A temp directory that work items need and the actual install directory. + install_list->AddCreateDirWorkItem(temp_dir); + install_list->AddCreateDirWorkItem(install_path); + + // Browse for all the files in archive, add work items to copy them while + // handling special cases of executables. + LOG(INFO) << "Looking for Chrome installation files under " << src_path; + std::wstring root_path(src_path); + file_util::AppendToPath(&root_path, L"*"); + WIN32_FIND_DATA find_file_data; + HANDLE file_handle = FindFirstFile(root_path.c_str(), &find_file_data); + BOOL ret = TRUE; + while (ret) { + LOG(INFO) << "directory found: " << find_file_data.cFileName; + // We do not want any directories starting with '.' + if ((wcslen(find_file_data.cFileName) <= 0) || + (find_file_data.cFileName[0] == '.')) { + LOG(INFO) << "Ignoring directory found: " << find_file_data.cFileName; + } else if (_wcsicmp(find_file_data.cFileName, + installer_util::kChromeExe) == 0) { + // Special case of chrome.exe. Delete any new_chrome.exe if present + // (we will create a new one if chrome.exe is in use) and then + // copy chrome.exe. + install_list->AddDeleteTreeWorkItem( + AppendPath(install_path, installer::kChromeNewExe), std::wstring()); + install_list->AddCopyTreeWorkItem( + AppendPath(src_path, installer_util::kChromeExe), + AppendPath(install_path, installer_util::kChromeExe), + temp_dir, WorkItem::RENAME_IF_IN_USE, + AppendPath(install_path, installer::kChromeNewExe)); + } else if (_wcsicmp(find_file_data.cFileName, + installer::kWowHelperExe) == 0) { + // Special case of wow_helper.exe which is required only on 64 bit + // systems. This exe runs only for a short time when Chrome starts so + // it should not be locked most of the times and can be overwritten + // directly. + if (Is64bit()) { + install_list->AddCopyTreeWorkItem( + AppendPath(src_path, installer::kWowHelperExe), + AppendPath(install_path, installer::kWowHelperExe), + temp_dir, WorkItem::ALWAYS); + } + } else { + // In all other cases just copy the file/directory to install location. + install_list->AddCopyTreeWorkItem( + AppendPath(src_path, find_file_data.cFileName), + AppendPath(install_path, find_file_data.cFileName), + temp_dir, WorkItem::ALWAYS); // Always overwrite. + } + ret = FindNextFile(file_handle, &find_file_data); + } + FindClose(file_handle); + + // Copy installer in install directory and + // add shortcut in Control Panel->Add/Remove Programs. + AddInstallerCopyTasks(exe_path, archive_path, temp_dir, install_path, + new_version.GetString(), install_list.get()); + AddUninstallShortcutWorkItems(reg_root, exe_path, install_path, + new_version.GetString(), install_list.get()); + + // Delete any old_chrome.exe if present. + install_list->AddDeleteTreeWorkItem( + AppendPath(install_path, installer::kChromeOldExe), std::wstring()); + + // Create Google Update key (if not already present) and set the new Chrome + // version as last step. + std::wstring chrome_google_update_key = + InstallUtil::GetChromeGoogleUpdateKey(); + install_list->AddCreateRegKeyWorkItem(reg_root, chrome_google_update_key); + install_list->AddSetRegValueWorkItem(reg_root, chrome_google_update_key, + google_update::kRegNameField, + installer_util::kChrome, + false); // Don't overwrite. + install_list->AddSetRegValueWorkItem(reg_root, chrome_google_update_key, + google_update::kRegVersionField, + new_version.GetString(), + true); // overwrite version + + // Perform install operations. + if (!install_list->Do()) { + LOG(ERROR) << "install failed, roll back... "; + install_list->Rollback(); + return false; + } + + return true; +} diff --git a/chrome/installer/setup/main.cc b/chrome/installer/setup/main.cc new file mode 100644 index 0000000..233408b --- /dev/null +++ b/chrome/installer/setup/main.cc @@ -0,0 +1,389 @@ +// 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 <string> + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/registry.h" +#include "base/string_util.h" +#include "chrome/installer/setup/setup.h" +#include "chrome/installer/setup/setup_constants.h" +#include "chrome/installer/setup/uninstall.h" +#include "chrome/installer/util/delete_tree_work_item.h" +#include "chrome/installer/util/helper.h" +#include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/logging_installer.h" +#include "chrome/installer/util/lzma_util.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/shell_util.h" +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/work_item.h" +#include "third_party/bspatch/mbspatch.h" + +namespace { + +// Checks if the current system is running Windows XP or later. We are not +// supporting Windows 2K for beta release. +bool IsWindowsXPorLater() { + OSVERSIONINFOEX osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + GetVersionEx((OSVERSIONINFO *) &osvi); + + // Windows versioning scheme doesn't seem very clear but here is what + // the code is checking as the minimum version required for Chrome: + // * Major > 5 is Vista or later so no further checks for Service Pack + // * Major = 5 && Minor > 1 is Windows Server 2003 so again no SP checks + // * Major = 5 && Minor = 1 is WinXP so check for SP1 or later + LOG(INFO) << "Windows Version: Major - " << osvi.dwMajorVersion + << " Minor - " << osvi.dwMinorVersion + << " Service Pack Major - " << osvi.wServicePackMajor + << " Service Pack Minor - " << osvi.wServicePackMinor; + if ((osvi.dwMajorVersion > 5) || // Vista or later + ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion > 1)) || // Win 2003 + ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 1) && + (osvi.wServicePackMajor >= 1))) { // WinXP with SP1 + return true; + } + return false; +} + +// Applies a binary patch to existing Chrome installer archive on the system. +// Uses bspatch library. +int PatchArchiveFile(bool system_install, const std::wstring& archive_path, + const std::wstring& uncompressed_archive, + const installer::Version* installed_version) { + std::wstring existing_archive = + installer::GetChromeInstallPath(system_install); + file_util::AppendToPath(&existing_archive, + installed_version->GetString()); + file_util::AppendToPath(&existing_archive, installer::kInstallerDir); + file_util::AppendToPath(&existing_archive, installer::kChromeArchive); + + std::wstring patch_archive(archive_path); + file_util::AppendToPath(&patch_archive, installer::kChromePatchArchive); + + LOG(INFO) << "Applying patch " << patch_archive + << " to file " << existing_archive + << " and generating file " << uncompressed_archive; + return ApplyBinaryPatch(WideToUTF8(existing_archive).c_str(), + WideToUTF8(patch_archive).c_str(), + WideToUTF8(uncompressed_archive).c_str()); +} + + +// This method unpacks and uncompresses the given archive file. For Chrome +// install we are creating a uncompressed archive that contains all the files +// needed for the installer. This uncompressed archive is later compressed. +// +// This method first uncompresses archive specified by parameter "archive" +// and assumes that it will result in an uncompressed full archive file +// (chrome.7z) or uncompressed patch archive file (patch.7z). If it is patch +// archive file, the patch is applied to the old archive file that should be +// present on the system already. As the final step the new archive file +// is unpacked in the path specified by parameter "path". +DWORD UnPackArchive(const std::wstring& archive, bool system_install, + const installer::Version* installed_version, + const std::wstring& temp_path, const std::wstring& path, + bool& incremental_install) { + DWORD ret = NO_ERROR; + installer::LzmaUtil util; + // First uncompress the payload. This could be a differential + // update (patch.7z) or full archive (chrome.7z). If this uncompress fails + // return with error. + LOG(INFO) << "Opening archive " << archive; + if ((ret = util.OpenArchive(archive)) != NO_ERROR) { + LOG(ERROR) << "unable to open install archive: " << archive; + } else { + LOG(INFO) << "Uncompressing archive to path " << temp_path; + if ((ret = util.UnPack(temp_path)) != NO_ERROR) { + LOG(ERROR) << "error during uncompression: " << ret; + } + util.CloseArchive(); + } + if (ret != NO_ERROR) + return ret; + + std::wstring archive_name = file_util::GetFilenameFromPath(archive); + std::wstring uncompressed_archive(temp_path); + file_util::AppendToPath(&uncompressed_archive, installer::kChromeArchive); + // Check if this is differential update and if it is, patch it to the + // installer archive that should already be on the machine. + std::wstring prefix = installer::kChromeCompressedPatchArchivePrefix; + if ((archive_name.size() >= prefix.size()) && + (std::equal(prefix.begin(), prefix.end(), archive_name.begin(), + CaseInsensitiveCompare<wchar_t>()))) { + LOG(INFO) << "Differential patch found. Applying to existing archive."; + incremental_install = true; + if (!installed_version) { + LOG(ERROR) << "Can not use differential update when Chrome is not " + << "installed on the system."; + return 1; + } + if (PatchArchiveFile(system_install, temp_path, uncompressed_archive, + installed_version)) { + LOG(ERROR) << "Binary patching failed."; + return 1; + } + } + + // If we got the uncompressed archive, lets unpack it + LOG(INFO) << "Opening archive " << uncompressed_archive; + if ((ret = util.OpenArchive(uncompressed_archive)) != NO_ERROR) { + LOG(ERROR) << "unable to open install archive: " << + uncompressed_archive; + } else { + LOG(INFO) << "Unpacking archive to path " << path; + if ((ret = util.UnPack(path)) != NO_ERROR) { + LOG(ERROR) << "error during uncompression: " << ret; + } + util.CloseArchive(); + } + + return ret; +} + + +// Find the version of Chrome from an install source directory. +// Chrome_path should contain a complete and unpacked install package (i.e. +// a Chrome directory under which there is a version folder). +// Returns the version or NULL if no version is found. +installer::Version* GetVersionFromDir(const std::wstring& chrome_path) { + LOG(INFO) << "Looking for Chrome version folder under " << chrome_path; + std::wstring root_path(chrome_path); + file_util::AppendToPath(&root_path, L"*"); + + WIN32_FIND_DATA find_file_data; + HANDLE file_handle = FindFirstFile(root_path.c_str(), &find_file_data); + BOOL ret = TRUE; + installer::Version *version = NULL; + // Here we are assuming that the installer we have is really valid so there + // can not be two version directories. We exit as soon as we find a valid + // version directory. + while (ret) { + if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + LOG(INFO) << "directory found: " << find_file_data.cFileName; + version = + installer::Version::GetVersionFromString(find_file_data.cFileName); + if (version) break; + } + ret = FindNextFile(file_handle, &find_file_data); + } + FindClose(file_handle); + + return version; +} + +// This method checks if we need to change "ap" key in Google Update to try +// full installer as fall back method in case incremental installer fails. +// - If incremental installer fails we append a magic string ("-full"), if +// it is not present already, so that Google Update server next time will send +// full installer to update Chrome on the local machine +// - If we are currently running full installer, we remove this magic +// string (if it is present) regardless of whether installer failed or not. +// There is no fall-back for full installer :) +void ResetGoogleUpdateApKey(bool system_install, bool incremental_install, + installer_util::InstallStatus install_status) { + HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + RegKey key; + std::wstring ap_key_value; + std::wstring chrome_google_update_state_key( + google_update::kRegPathClientState); + chrome_google_update_state_key.append(L"\\"); + chrome_google_update_state_key.append(google_update::kChromeGuid); + if (!key.Open(reg_root, chrome_google_update_state_key.c_str(), + KEY_ALL_ACCESS) || !key.ReadValue(google_update::kRegApFieldName, + &ap_key_value)) { + LOG(INFO) << "Application key not found. Returning without changing it."; + key.Close(); + return; + } + + std::wstring new_value = InstallUtil::GetNewGoogleUpdateApKey( + incremental_install, install_status, ap_key_value); + if ((new_value.compare(ap_key_value) != 0) && + !key.WriteValue(google_update::kRegApFieldName, new_value.c_str())) { + LOG(ERROR) << "Failed to write value " << new_value + << " to the registry field " << google_update::kRegApFieldName; + } + key.Close(); +} +} // namespace + + +int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, + wchar_t* command_line, int show_command) { + CommandLine parsed_command_line; + installer::InitInstallerLogging(parsed_command_line); + + // Check to make sure current system is WinXP or later. If not, log + // error message and get out. + if (!IsWindowsXPorLater()) { + LOG(ERROR) << "Chrome only supports Windows XP or later"; + return installer_util::OS_NOT_SUPPORTED; + } + + // Initialize COM for use later. + if (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) != S_OK) { + LOG(ERROR) << "COM initialization failed."; + return installer_util::OS_ERROR; + } + + bool system_install = + parsed_command_line.HasSwitch(installer_util::switches::kSystemInstall); + LOG(INFO) << "system install is " << system_install; + + // Check the existing version installed. + scoped_ptr<installer::Version> + installed_version(InstallUtil::GetChromeVersion(system_install)); + if (installed_version.get()) { + LOG(INFO) << "version on the system: " << installed_version->GetString(); + } + + // If --register-chrome-browser option is specified, register all + // Chrome protocol/file associations as well as register it as a valid + // browser for StarMenu->Internet shortcut. This option should only + // be used when setup.exe is launched with admin rights. We do not + // make any user specific changes in this option. + if (parsed_command_line.HasSwitch( + installer_util::switches::kRegisterChromeBrowser)) { + std::wstring chrome_exe(parsed_command_line.GetSwitchValue( + installer_util::switches::kRegisterChromeBrowser)); + return ShellUtil::AddChromeToSetAccessDefaults(chrome_exe, true); + } + + installer_util::InstallStatus install_status = installer_util::UNKNOWN_STATUS; + if (parsed_command_line.HasSwitch(installer_util::switches::kUninstall)) { + bool remove_all = true; + if (parsed_command_line.HasSwitch( + installer_util::switches::kDoNotRemoveSharedItems)) + remove_all = false; + // If --uninstall option is given, uninstall chrome + LOG(INFO) << "Uninstalling Chome"; + if (!installed_version.get()) { + LOG(ERROR) << "No Chrome installation found for uninstall"; + install_status = installer_util::CHROME_NOT_INSTALLED; + } else { + install_status = installer_setup::UninstallChrome( + parsed_command_line.program(), system_install, + *installed_version, remove_all); + } + } else { + // If --uninstall option is not specified, we assume it is install case. + // For install the default location for chrome.packed.7z is in current + // folder, so get that value first. + std::wstring archive_path = + file_util::GetDirectoryFromPath(parsed_command_line.program()); + file_util::AppendToPath(&archive_path, + std::wstring(installer::kChromeCompressedArchive)); + // If --install-archive is given, get the user specified value + if (parsed_command_line.HasSwitch( + installer_util::switches::kInstallArchive)) { + archive_path = parsed_command_line.GetSwitchValue( + installer_util::switches::kInstallArchive); + } + LOG(INFO) << "Archive found to install Chrome " << archive_path; + + // Create a temp folder where we will unpack Chrome archive. If it fails, + // then we are doomed so return immediately and no cleanup is required. + std::wstring install_temp_path; + if (!file_util::CreateNewTempDirectory(std::wstring(L"chrome_"), + &install_temp_path)) { + LOG(ERROR) << "can not create temporary path"; + return installer_util::TEMP_DIR_FAILED; + } + LOG(INFO) << "created path " << install_temp_path; + std::wstring unpack_path(install_temp_path); + file_util::AppendToPath(&unpack_path, + std::wstring(installer::kInstallSourceDir)); + + bool incremental_install = false; + if (UnPackArchive(archive_path, system_install, installed_version.get(), + install_temp_path, unpack_path, incremental_install)) { + install_status = installer_util::UNCOMPRESSION_FAILED; + } else { + LOG(INFO) << "unpacked to " << unpack_path; + std::wstring src_path(unpack_path); + file_util::AppendToPath(&src_path, + std::wstring(installer::kInstallSourceChromeDir)); + scoped_ptr<installer::Version> + installer_version(GetVersionFromDir(src_path)); + if (!installer_version.get()) { + LOG(ERROR) << "didn't find any valid version in installer"; + install_status = installer_util::INVALID_ARCHIVE; + } else { + LOG(INFO) << "version to be installed: " << + installer_version->GetString(); + if (installed_version.get() && + installed_version->IsHigherThan(installer_version.get())) { + LOG(ERROR) << "Higher version is already installed."; + install_status = installer_util::HIGHER_VERSION_EXISTS; + } else { + // We want to keep uncompressed archive (chrome.7z) that we get after + // uncompressing and binary patching. Get the location for this file. + std::wstring archive_to_copy(install_temp_path); + file_util::AppendToPath(&archive_to_copy, + std::wstring(installer::kChromeArchive)); + install_status = installer::InstallOrUpdateChrome( + parsed_command_line.program(), archive_to_copy, + install_temp_path, system_install, + *installer_version, installed_version.get()); + if (install_status == installer_util::FIRST_INSTALL_SUCCESS) { + LOG(INFO) << "First install successful. Launching Chrome."; + installer::LaunchChrome(system_install); + } + } + } + } + + // Delete install temporary directory. + LOG(INFO) << "Deleting temporary directory " << install_temp_path; + scoped_ptr<DeleteTreeWorkItem> delete_tree( + WorkItem::CreateDeleteTreeWorkItem(install_temp_path, + std::wstring())); + delete_tree->Do(); + + ResetGoogleUpdateApKey(system_install, incremental_install, install_status); + + // TBD: The previous installs/updates may leave some temporary files + // that were not deleted when the installs/updates exited, probably due + // to a crash. Try delete those temporary files again? + } + + CoUninitialize(); + if (InstallUtil::InstallSuccessful(install_status)) + return 0; // For Google Update's benefit we need to return 0 for success + // cases. + else + return install_status; +} diff --git a/chrome/installer/setup/setup.cc b/chrome/installer/setup/setup.cc new file mode 100644 index 0000000..7452f10 --- /dev/null +++ b/chrome/installer/setup/setup.cc @@ -0,0 +1,290 @@ +// 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 <shlobj.h> + +#include "chrome/installer/setup/setup.h" + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/registry.h" +#include "base/string_util.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/installer/setup/setup_constants.h" +#include "chrome/installer/util/create_reg_key_work_item.h" +#include "chrome/installer/util/l10n_string_util.h" +#include "chrome/installer/util/logging_installer.h" +#include "chrome/installer/util/helper.h" +#include "chrome/installer/util/shell_util.h" +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/version.h" +#include "chrome/installer/util/work_item_list.h" + +#include "setup_strings.h" + +namespace { + +void AddChromeToMediaPlayerList() { + std::wstring reg_path(installer::kMediaPlayerRegPath); + // registry paths can also be appended like file system path + file_util::AppendToPath(®_path, installer_util::kChromeExe); + LOG(INFO) << "Adding Chrome to Media player list at " << reg_path; + scoped_ptr<WorkItem> work_item(WorkItem::CreateCreateRegKeyWorkItem( + HKEY_LOCAL_MACHINE, reg_path)); + + // if the operation fails we log the error but still continue + if (!work_item.get()->Do()) + LOG(ERROR) << "Couldn't add Chrome to media player inclusion list."; + +} + +// If we ever rename Chrome shortcuts under Windows Start menu, this +// function deletes any of the old Chrome shortcuts if they exist. This +// function will probably contain hard coded names of old shortcuts as they +// will not longer be used anywhere else. +// Method returns true if it finds and deletes successfully any old shortcut, +// in all other cases it returns false. +bool DeleteOldShortcuts(const std::wstring shortcut_path) { + // Check for the existence of shortcuts when they were still created under + // Start->Programs->Chrome (as opposed to Start->Programs->Google Chrome). + std::wstring shortcut_folder(shortcut_path); + file_util::AppendToPath(&shortcut_folder, L"Chrome"); + if (file_util::PathExists(shortcut_folder)) { + LOG(INFO) << "Old shortcut path " << shortcut_folder << " exists."; + return file_util::Delete(shortcut_folder, true); + } + return false; +} + +// Update shortcuts that are created by chrome.exe during first run, but +// we take care of updating them in case the location of chrome.exe changes. +void UpdateChromeExeShortcuts(const std::wstring& chrome_exe) { + std::wstring desktop_shortcut, ql_shortcut, shortcut_name; + if (!ShellUtil::GetQuickLaunchPath(&ql_shortcut) || + !ShellUtil::GetDesktopPath(&desktop_shortcut) || + !ShellUtil::GetChromeShortcutName(&shortcut_name)) + return; + // Migrate the old shortcuts from Chrome.lnk to the localized name. + std::wstring old_ql_shortcut = ql_shortcut; + file_util::AppendToPath(&old_ql_shortcut, L"Chrome.lnk"); + file_util::AppendToPath(&ql_shortcut, shortcut_name); + std::wstring old_desktop_shortcut = desktop_shortcut; + file_util::AppendToPath(&old_desktop_shortcut, L"Chrome.lnk"); + file_util::AppendToPath(&desktop_shortcut, shortcut_name); + + if (file_util::Move(old_ql_shortcut, ql_shortcut)) { + // Notify the Windows Shell that we renamed the file so it can remove the + // old icon. It's safe to not cehck for MAX_PATH because file_util::Move + // does the check for us. + SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATH, old_ql_shortcut.c_str(), + ql_shortcut.c_str()); + } + if (file_util::Move(old_desktop_shortcut, desktop_shortcut)) { + SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATH, old_desktop_shortcut.c_str(), + desktop_shortcut.c_str()); + } + + // Go ahead and update the shortcuts if they exist. + ShellUtil::UpdateChromeShortcut(chrome_exe, ql_shortcut, false); + ShellUtil::UpdateChromeShortcut(chrome_exe, desktop_shortcut, false); +} + +// This method creates Chrome shortcuts in Start->Programs for all users or +// only for current user depending on whether it is system wide install or +// user only install. +// +// If first_install is true, it creates shortcuts for launching and +// uninstalling chrome. +// If first_install is false, the function only updates the shortcut for +// uninstalling chrome. According to +// http://blogs.msdn.com/oldnewthing/archive/2005/11/24/496690.aspx, +// updating uninstall shortcut should not trigger Windows "new application +// installed" notification. +// +// If the shortcuts do not exist, the function does not recreate them during +// update. +bool CreateOrUpdateChromeShortcuts(const std::wstring& exe_path, + bool system_install, + installer_util::InstallStatus install_status, + const std::wstring& install_path, + const std::wstring& new_version) { + std::wstring shortcut_path; + int dir_enum = (system_install) ? base::DIR_COMMON_START_MENU : + base::DIR_START_MENU; + if (!PathService::Get(dir_enum, &shortcut_path)) { + LOG(ERROR) << "Failed to get location for shortcut."; + return false; + } + + // Check for existence of old shortcuts + bool old_shortcuts_existed = DeleteOldShortcuts(shortcut_path); + + // The location of Start->Programs->Google Chrome folder + const std::wstring& product_name = + installer_util::GetLocalizedString(IDS_PRODUCT_NAME_BASE); + file_util::AppendToPath(&shortcut_path, product_name); + + // Create/update Chrome link (points to chrome.exe) & Uninstall Chrome link + // (which points to setup.exe) under this folder only if: + // - This is a new install or install repair + // OR + // - The shortcut already exists in case of updates (user may have deleted + // shortcuts since our install. So on updates we only update if shortcut + // already exists) + bool ret1 = true; + std::wstring chrome_link(shortcut_path); // Chrome link (launches Chrome) + file_util::AppendToPath(&chrome_link, product_name + L".lnk"); + std::wstring chrome_exe(install_path); // Chrome link target + file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); + + if ((install_status == installer_util::FIRST_INSTALL_SUCCESS) || + (install_status == installer_util::INSTALL_REPAIRED) || + (old_shortcuts_existed)) { + if (!file_util::PathExists(shortcut_path)) + file_util::CreateDirectoryW(shortcut_path); + + LOG(INFO) << "Creating shortcut to " << chrome_exe << " at " << chrome_link; + ShellUtil::UpdateChromeShortcut(chrome_exe, chrome_link, true); + } else if (file_util::PathExists(chrome_link)) { + LOG(INFO) << "Updating shortcut at " << chrome_link + << " to point to " << chrome_exe; + ShellUtil::UpdateChromeShortcut(chrome_exe, chrome_link, false); + } + + // Create/update uninstall link + bool ret2 = true; + std::wstring uninstall_link(shortcut_path); // Uninstall Chrome link + + file_util::AppendToPath(&uninstall_link, + installer_util::GetLocalizedString(IDS_UNINSTALL_CHROME_BASE) + L".lnk"); + if ((install_status == installer_util::FIRST_INSTALL_SUCCESS) || + (install_status == installer_util::INSTALL_REPAIRED) || + (old_shortcuts_existed) || + (file_util::PathExists(uninstall_link))) { + if (!file_util::PathExists(shortcut_path)) + file_util::CreateDirectoryW(shortcut_path); + std::wstring setup_exe(installer::GetInstallerPathUnderChrome(install_path, + new_version)); + file_util::AppendToPath(&setup_exe, + file_util::GetFilenameFromPath(exe_path)); + std::wstring arguments(L" --"); + arguments.append(installer_util::switches::kUninstall); + LOG(INFO) << "Creating/updating uninstall link at " << uninstall_link; + ret2 = file_util::CreateShortcutLink(setup_exe.c_str(), + uninstall_link.c_str(), + install_path.c_str(), + arguments.c_str(), + NULL, + setup_exe.c_str(), + 0); + } + + // Update Desktop and Quick Launch shortcuts (only if they already exist) + UpdateChromeExeShortcuts(chrome_exe); + + return ret1 && ret2; +} +} // namespace + + +std::wstring installer::GetInstallerPathUnderChrome( + const std::wstring& install_path, const std::wstring& new_version) { + std::wstring installer_path(install_path); + file_util::AppendToPath(&installer_path, new_version); + file_util::AppendToPath(&installer_path, installer::kInstallerDir); + return installer_path; +} + + +installer_util::InstallStatus installer::InstallOrUpdateChrome( + const std::wstring& exe_path, const std::wstring& archive_path, + const std::wstring& install_temp_path, bool system_install, + const Version& new_version, const Version* installed_version) { + + std::wstring install_path(GetChromeInstallPath(system_install)); + if (install_path.empty()) { + LOG(ERROR) << "Couldn't get installation destination path"; + return installer_util::INSTALL_FAILED; + } else { + LOG(INFO) << "install destination path: " << install_path; + } + + std::wstring src_path(install_temp_path); + file_util::AppendToPath(&src_path, std::wstring(kInstallSourceDir)); + file_util::AppendToPath(&src_path, std::wstring(kInstallSourceChromeDir)); + + HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + bool install_success = InstallNewVersion(exe_path, archive_path, src_path, + install_path, install_temp_path, reg_root, new_version); + + installer_util::InstallStatus result; + if (!install_success) { + LOG(ERROR) << "Install failed."; + result = installer_util::INSTALL_FAILED; + } else { + if (!installed_version) { + LOG(INFO) << "First install of version " << new_version.GetString(); + result = installer_util::FIRST_INSTALL_SUCCESS; + } else if (new_version.GetString() == installed_version->GetString()) { + LOG(INFO) << "Install repaired of version " << new_version.GetString(); + result = installer_util::INSTALL_REPAIRED; + } else if (new_version.IsHigherThan(installed_version)) { + LOG(INFO) << "Version updated to " << new_version.GetString(); + result = installer_util::NEW_VERSION_UPDATED; + } else { + NOTREACHED(); + } + + if (!CreateOrUpdateChromeShortcuts(exe_path, system_install, result, + install_path, new_version.GetString())) + LOG(WARNING) << "Failed to create/update start menu shortcut."; + + std::wstring chrome_exe(install_path); + file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); + if (result == installer_util::FIRST_INSTALL_SUCCESS || + result == installer_util::INSTALL_REPAIRED) { + // Try to add Chrome to Media Player shim inclusion list. We don't do any + // error checking here because this operation will fail if user doesn't + // have admin rights and we want to ignore the error. + AddChromeToMediaPlayerList(); + + // We try to register Chrome as a valid browser on local machine. This + // will work only if current user has admin rights. + LOG(INFO) << "Registering Chrome as browser"; + ShellUtil::RegisterStatus ret = + ShellUtil::AddChromeToSetAccessDefaults(chrome_exe, true); + LOG(ERROR) << "Return status of Chrome browser registration " << ret; + } else { + UpdateChromeExeShortcuts(chrome_exe); + RemoveOldVersionDirs(install_path, new_version.GetString()); + } + } + + return result; +} diff --git a/chrome/installer/setup/setup.exe.manifest b/chrome/installer/setup/setup.exe.manifest new file mode 100644 index 0000000..28469a3 --- /dev/null +++ b/chrome/installer/setup/setup.exe.manifest @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <ms_asmv2:trustInfo xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
+ <ms_asmv2:security>
+ <ms_asmv2:requestedPrivileges>
+ <ms_asmv2:requestedExecutionLevel level="asInvoker">
+ </ms_asmv2:requestedExecutionLevel>
+ </ms_asmv2:requestedPrivileges>
+ </ms_asmv2:security>
+ </ms_asmv2:trustInfo>
+</assembly>
diff --git a/chrome/installer/setup/setup.h b/chrome/installer/setup/setup.h new file mode 100644 index 0000000..0f3ebc5 --- /dev/null +++ b/chrome/installer/setup/setup.h @@ -0,0 +1,104 @@ +// 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 the specification of setup main functions. + +#ifndef CHROME_INSTALLER_SETUP_SETUP_H__ +#define CHROME_INSTALLER_SETUP_SETUP_H__ + +#include <string> +#include <windows.h> + +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/version.h" + +namespace installer { +// Get path to the installer under Chrome version folder +// (for example <path>\Google\Chrome\<Version>\installer) +std::wstring GetInstallerPathUnderChrome(const std::wstring& install_path, + const std::wstring& new_version); + +// This function installs or updates a new version of Chrome. It returns +// install status (failed, new_install, updated etc). +// +// exe_path: Path to the executable (setup.exe) as it will be copied +// to Chrome install folder after install is complete +// archive_path: Path to the archive (chrome.7z) as it will be copied +// to Chrome install folder after install is complete +// install_temp_path: working directory used during install/update. It should +// also has a sub dir source that contains a complete +// and unpacked Chrome package. +// system_install: if true, the function performs a system wide install/update. +// Otherwise it installs/updates Chrome for the current user. +// new_version: new Chrome version that needs to be installed +// installed_version: currently installed version of Chrome, if any, or +// NULL otherwise +// +// Note: since caller unpacks Chrome to install_temp_path\source, the caller +// is responsible for cleaning up install_temp_path. +installer_util::InstallStatus InstallOrUpdateChrome( + const std::wstring& exe_path, const std::wstring& archive_path, + const std::wstring& install_temp_path, bool system_install, + const Version& new_version, const Version* installed_version); + +// This function installs a new version of Chrome to the specified location. +// It returns true if install was successful and false in case of an error. +// +// exe_path: Path to the executable (setup.exe) as it will be copied +// to Chrome install folder after install is complete +// archive_path: Path to the archive (chrome.7z) as it will be copied +// to Chrome install folder after install is complete +// src_path: the path that contains a complete and unpacked Chrome package +// to be installed. +// install_path: the destination path for Chrome to be installed to. This +// path does not need to exist. +// temp_dir: the path of working directory used during installation. This path +// does not need to exist. +// reg_root: the root of registry where the function applies settings for the +// new Chrome version. It should be either HKLM or HKCU. +// new_version: new Chrome version that needs to be installed +// +// This function makes best effort to do installation in a transactional +// manner. If failed it tries to rollback all changes on the file system +// and registry. For example, if install_path exists before calling the +// function, it rolls back all new file and directory changes under +// install_path. If install_path does not exist before calling the function +// (typical new install), the function creates install_path during install +// and removes the whole directory during rollback. +bool InstallNewVersion(const std::wstring& exe_path, + const std::wstring& archive_path, + const std::wstring& src_path, + const std::wstring& install_path, + const std::wstring& temp_dir, + const HKEY reg_root, + const Version& new_version); + +} + +#endif // CHROME_INSTALLER_SETUP_SETUP_H__ diff --git a/chrome/installer/setup/setup.ico b/chrome/installer/setup/setup.ico Binary files differnew file mode 100644 index 0000000..5ecfcbd --- /dev/null +++ b/chrome/installer/setup/setup.ico diff --git a/chrome/installer/setup/setup.rc b/chrome/installer/setup/setup.rc new file mode 100644 index 0000000..2423a76 --- /dev/null +++ b/chrome/installer/setup/setup.rc @@ -0,0 +1,82 @@ +// Microsoft Visual C++ generated resource script. +// +#include "setup_resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "setup_resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""setup_exe_version.rc""\r\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SETUP ICON "setup.ico" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_UNINSTALL_SURVEY_URL "http://go/chromeuninstall?hl=$1&contact_type=uninstall" +END + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "setup_exe_version.rc" + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/chrome/installer/setup/setup.vcproj b/chrome/installer/setup/setup.vcproj new file mode 100644 index 0000000..e6c6abf --- /dev/null +++ b/chrome/installer/setup/setup.vcproj @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="setup" + ProjectGUID="{21C76E6E-8B38-44D6-8148-B589C13B9554}" + RootNamespace="setup" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + <ToolFile + RelativePath="..\..\tools\build\win\version.rules" + /> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="1" + InheritedPropertySheets=".\setup_debug.vsprops;..\util\using_util.vsprops;..\util\prebuild\util_prebuild.vsprops" + > + <Tool + Name="Version" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCLinkerTool" + /> + <Tool + Name="VCManifestTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="1" + InheritedPropertySheets=".\setup_release.vsprops;..\util\using_util.vsprops;..\util\prebuild\util_prebuild.vsprops" + > + <Tool + Name="Version" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCLinkerTool" + /> + <Tool + Name="VCManifestTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="resources" + > + <File + RelativePath=".\setup_resource.h" + > + </File> + <File + RelativePath=".\setup.ico" + > + </File> + <File + RelativePath=".\setup.rc" + > + </File> + <File + RelativePath=".\setup_exe_version.rc.version" + > + </File> + <File + RelativePath="$(IntDir)\..\util_prebuild\setup_strings.h" + > + </File> + <File + RelativePath="$(IntDir)\..\util_prebuild\setup_strings.rc" + > + </File> + </Filter> + <File + RelativePath=".\install.cc" + > + </File> + <File + RelativePath=".\main.cc" + > + </File> + <File + RelativePath=".\setup.cc" + > + </File> + <File + RelativePath=".\setup.h" + > + </File> + <File + RelativePath=".\setup_constants.cc" + > + </File> + <File + RelativePath=".\setup_constants.h" + > + </File> + <File + RelativePath=".\uninstall.cc" + > + </File> + <File + RelativePath=".\uninstall.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/installer/setup/setup.vsprops b/chrome/installer/setup/setup.vsprops new file mode 100644 index 0000000..53258e4 --- /dev/null +++ b/chrome/installer/setup/setup.vsprops @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="setup" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops" + > + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="$(OutDir);$(IntDir)" + EnableIntrinsicFunctions="true" + BasicRuntimeChecks="0" + BufferSecurityCheck="false" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="msi.lib" + GenerateMapFile="true" + SubSystem="2" + OptimizeForWindows98="1" + /> + <Tool + Name="VCResourceCompilerTool" + AdditionalIncludeDirectories="$(OutDir);$(IntDir)" + /> + <Tool + Name="VCManifestTool" + AdditionalManifestFiles="$(SolutionDir)installer\setup\setup.exe.manifest" + /> +</VisualStudioPropertySheet> diff --git a/chrome/installer/setup/setup_constants.cc b/chrome/installer/setup/setup_constants.cc new file mode 100644 index 0000000..3397836 --- /dev/null +++ b/chrome/installer/setup/setup_constants.cc @@ -0,0 +1,49 @@ +// 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/setup/setup_constants.h" + +namespace installer { +// Elements that makes up install target path. +const wchar_t kChromeOldExe[] = L"old_chrome.exe"; +const wchar_t kChromeNewExe[] = L"new_chrome.exe"; +const wchar_t kWowHelperExe[] = L"wow_helper.exe"; +const wchar_t kChromeArchive[] = L"chrome.7z"; +const wchar_t kChromePatchArchive[] = L"patch.7z"; +const wchar_t kChromeCompressedArchive[] = L"chrome.packed.7z"; +const wchar_t kChromeCompressedPatchArchivePrefix[] = L"patch"; + +// Sub directory of install source package under install temporary directory. +const wchar_t kInstallSourceDir[] = L"source"; +const wchar_t kInstallSourceChromeDir[] = L"Chrome-bin"; + +const wchar_t kInstallerDir[] = L"Installer"; + +const wchar_t kMediaPlayerRegPath[] = L"Software\\Microsoft\\MediaPlayer\\ShimInclusionList"; +} // namespace installer diff --git a/chrome/installer/setup/setup_constants.h b/chrome/installer/setup/setup_constants.h new file mode 100644 index 0000000..7e4bc48 --- /dev/null +++ b/chrome/installer/setup/setup_constants.h @@ -0,0 +1,53 @@ +// 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 by Chrome installer. + +#ifndef CHROME_INSTALLER_SETUP_SETUP_CONSTANTS_H__ +#define CHROME_INSTALLER_SETUP_SETUP_CONSTANTS_H__ + +namespace installer { + +extern const wchar_t kChromeOldExe[]; +extern const wchar_t kChromeNewExe[]; +extern const wchar_t kWowHelperExe[]; +extern const wchar_t kChromeArchive[]; +extern const wchar_t kChromePatchArchive[]; +extern const wchar_t kChromeCompressedArchive[]; +extern const wchar_t kChromeCompressedPatchArchivePrefix[]; + +extern const wchar_t kInstallSourceDir[]; +extern const wchar_t kInstallSourceChromeDir[]; + +extern const wchar_t kInstallerDir[]; + +extern const wchar_t kMediaPlayerRegPath[]; +} // namespace installer + +#endif // CHROME_INSTALLER_SETUP_SETUP_CONSTANTS_H__ diff --git a/chrome/installer/setup/setup_debug.vsprops b/chrome/installer/setup/setup_debug.vsprops new file mode 100644 index 0000000..873c6f1 --- /dev/null +++ b/chrome/installer/setup/setup_debug.vsprops @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="setup_debug" + InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\setup.vsprops" + > +</VisualStudioPropertySheet> diff --git a/chrome/installer/setup/setup_exe_version.rc.version b/chrome/installer/setup/setup_exe_version.rc.version new file mode 100644 index 0000000..5c41cf0 --- /dev/null +++ b/chrome/installer/setup/setup_exe_version.rc.version @@ -0,0 +1,40 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @MAJOR@,@MINOR@,@BUILD@,@PATCH@ + PRODUCTVERSION @MAJOR@,@MINOR@,@BUILD@,@PATCH@ + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "@COMPANY_FULLNAME@" + VALUE "FileDescription", "@PRODUCT_FULLNAME@" + VALUE "FileVersion", "@MAJOR@.@MINOR@.@BUILD@.@PATCH@" + VALUE "InternalName", "setup" + VALUE "LegalCopyright", "@COPYRIGHT@" + VALUE "ProductName", "@PRODUCT_FULLNAME@" + VALUE "ProductVersion", "@MAJOR@.@MINOR@.@BUILD@.@PATCH@" + VALUE "CompanyShortName", "@COMPANY_SHORTNAME@" + VALUE "ProductShortName", "@PRODUCT_SHORTNAME@" + VALUE "LastChange", "@LASTCHANGE@" + VALUE "Official Build", "@OFFICIAL_BUILD@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/chrome/installer/setup/setup_release.vsprops b/chrome/installer/setup/setup_release.vsprops new file mode 100644 index 0000000..ee6094c --- /dev/null +++ b/chrome/installer/setup/setup_release.vsprops @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="setup_release" + InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\setup.vsprops" + > +</VisualStudioPropertySheet> diff --git a/chrome/installer/setup/setup_resource.h b/chrome/installer/setup/setup_resource.h new file mode 100644 index 0000000..6915d21 --- /dev/null +++ b/chrome/installer/setup/setup_resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by setup.rc +// +#define IDI_SETUP 101 +#define IDS_UNINSTALL_SURVEY_URL 102 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc new file mode 100644 index 0000000..9403a8a --- /dev/null +++ b/chrome/installer/setup/uninstall.cc @@ -0,0 +1,303 @@ +// 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 the methods useful for uninstalling Chrome. + +#include "chrome/installer/setup/uninstall.h" + +#include <atlbase.h> +#include <windows.h> +#include <msi.h> + +#include "base/file_util.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 "base/wmi_util.h" +#include "chrome/app/google_update_settings.h" +#include "chrome/app/result_codes.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/installer/setup/setup_constants.h" +#include "chrome/installer/util/helper.h" +#include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/l10n_string_util.h" +#include "chrome/installer/util/logging_installer.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/shell_util.h" +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/version.h" + +#include "setup_resource.h" +#include "setup_strings.h" + +namespace { + +// This method deletes Chrome shortcut folder from Windows Start menu. It +// checks system_uninstall to see if the shortcut is in all users start menu +// or current user start menu. +void DeleteChromeShortcut(bool system_uninstall) { + std::wstring shortcut_path; + if (system_uninstall) { + PathService::Get(base::DIR_COMMON_START_MENU, &shortcut_path); + } else { + PathService::Get(base::DIR_START_MENU, &shortcut_path); + } + if (shortcut_path.empty()) { + LOG(ERROR) << "failed to get location for shortcut"; + } else { + file_util::AppendToPath(&shortcut_path, + installer_util::GetLocalizedString(IDS_PRODUCT_NAME_BASE)); + LOG(INFO) << "Deleting shortcut " << shortcut_path; + if (!file_util::Delete(shortcut_path, true)) + LOG(ERROR) << "Failed to delete folder: " << shortcut_path; + } +} + +// This method tries to delete a registry key and logs an error message +// in case of failure. It returns true if deletion is successful, +// otherwise false. +bool DeleteRegistryKey(RegKey& key, const std::wstring& key_path) { + LOG(INFO) << "Deleting registry key " << key_path; + if (!key.DeleteKey(key_path.c_str())) { + LOG(ERROR) << "Failed to delete registry key: " << key_path + << " and the error is " << InstallUtil::FormatLastWin32Error(); + return false; + } + return true; +} + +// This method tries to delete a registry value and logs an error message +// in case of failure. It returns true if deletion is successful, +// otherwise false. +bool DeleteRegistryValue(HKEY reg_root, const std::wstring& key_path, + const std::wstring& value_name) { + RegKey key(reg_root, key_path.c_str(), KEY_ALL_ACCESS); + LOG(INFO) << "Deleting registry value " << value_name; + if (!key.DeleteValue(value_name.c_str())) { + LOG(ERROR) << "Failed to delete registry value: " << value_name + << " and the error is " << InstallUtil::FormatLastWin32Error(); + return false; + } + return true; +} + +// This method checks if Chrome is currently running or if the user has +// cancelled the uninstall operation by clicking Cancel on the confirmation +// box that Chrome pops up. +installer_util::InstallStatus IsChromeActiveOrUserCancelled( + bool system_uninstall) { + static const std::wstring kCmdLineOptions(L" --uninstall"); + static const int32 kTimeOutMs = 30000; + int32 exit_code = ResultCodes::NORMAL_EXIT; + bool is_timeout = false; + + // We ignore all other errors such as whether launching chrome fails, + // whether chrome returns UNINSTALL_ERROR, etc. + LOG(INFO) << "Launching Chrome to do uninstall tasks."; + if (installer::LaunchChromeAndWaitForResult(system_uninstall, + kCmdLineOptions, + kTimeOutMs, + &exit_code, + &is_timeout)) { + if (is_timeout || exit_code == ResultCodes::UNINSTALL_CHROME_ALIVE) { + LOG(ERROR) << "Can't uninstall when chrome is still running"; + return installer_util::CHROME_RUNNING; + } else if (exit_code == ResultCodes::UNINSTALL_USER_CANCEL) { + LOG(INFO) << "User cancelled uninstall operation"; + return installer_util::UNINSTALL_CANCELLED; + } else if (exit_code == ResultCodes::UNINSTALL_ERROR) { + LOG(ERROR) << "chrome.exe reported error while uninstalling."; + return installer_util::UNINSTALL_FAILED; + } + } + + return installer_util::UNINSTALL_CONFIRMED; +} + +// Read the URL from the resource file and substitute the locale parameter +// with whatever Google Update tells us is the locale. In case we fail to find +// the locale, we use US English. +std::wstring GetUninstallSurveyUrl() { + const ATLSTRINGRESOURCEIMAGE* image = AtlGetStringResourceImage( + _AtlBaseModule.GetModuleInstance(), IDS_UNINSTALL_SURVEY_URL); + DCHECK(image); + std::wstring url = std::wstring(image->achString, image->nLength); + DCHECK(!url.empty()); + + std::wstring language; + if (!GoogleUpdateSettings::GetLanguage(&language)) + language = L"en-US"; // Default to US English. + + return ReplaceStringPlaceholders(url.c_str(), language.c_str(), NULL); +} + +// This method launches an uninstall survey and is called at the end of +// uninstall process. We are not doing any error checking here as it is +// not critical to have this survey. If we fail to launch it, we just +// ignore it silently. +void LaunchUninstallSurvey(const installer::Version& installed_version) { + // Send the Chrome version and OS version as params to the form. + // It would be nice to send the locale, too, but I don't see an + // easy way to get that in the existing code. It's something we + // can add later, if needed. + // We depend on installed_version.GetString() not having spaces or other + // characters that need escaping: 0.2.13.4. Should that change, we will + // need to escape the string before using it in a URL. + const std::wstring kVersionParam = L"crversion"; + const std::wstring kVersion = installed_version.GetString(); + const std::wstring kOSParam = L"os"; + std::wstring os_version = L"na"; + OSVERSIONINFO version_info; + version_info.dwOSVersionInfoSize = sizeof version_info; + if (GetVersionEx(&version_info)) { + os_version = StringPrintf(L"%d.%d.%d", + version_info.dwMajorVersion, + version_info.dwMinorVersion, + version_info.dwBuildNumber); + } + + std::wstring iexplore; + if (!PathService::Get(base::DIR_PROGRAM_FILES, &iexplore)) + return; + + file_util::AppendToPath(&iexplore, L"Internet Explorer"); + file_util::AppendToPath(&iexplore, L"iexplore.exe"); + + std::wstring command = iexplore + L" " + GetUninstallSurveyUrl() + L"&" + + kVersionParam + L"=" + kVersion + L"&" + kOSParam + L"=" + os_version; + int pid = 0; + WMIProcessUtil::Launch(command, &pid); +} + +// Uninstall Chrome specific Gears. First we find Gears MSI ProductId (that +// changes with every new version of Gears) using Gears MSI UpgradeCode (that +// does not change) and then uninstall Gears using API. +void UninstallGears() { + wchar_t product[39]; // GUID + '\0' + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); // Don't show any UI to user. + for (int i = 0; MsiEnumRelatedProducts(google_update::kGearsUpgradeCode, 0, i, + product) != ERROR_NO_MORE_ITEMS; ++i) { + LOG(INFO) << "Uninstalling Gears - " << product; + unsigned int ret = MsiConfigureProduct(product, INSTALLLEVEL_MAXIMUM, + INSTALLSTATE_ABSENT); + if (ret != ERROR_SUCCESS) + LOG(ERROR) << "Failed to uninstall Gears " << product + << " because of error " << ret; + } +} + +} // namespace + + +installer_util::InstallStatus installer_setup::UninstallChrome( + const std::wstring& exe_path, bool system_uninstall, + const installer::Version& installed_version, bool remove_all) { + installer_util::InstallStatus status = + IsChromeActiveOrUserCancelled(system_uninstall); + if (status == installer_util::CHROME_RUNNING || + status == installer_util::UNINSTALL_CANCELLED) + return status; + + // Uninstall Gears first. + UninstallGears(); + + // Chrome is not in use so lets uninstall Chrome by deleting various files + // and registry entries. Here we will just make best effort and keep going + // in case of errors. + // First delete shortcut from Start->Programs. + DeleteChromeShortcut(system_uninstall); + + // Delete the registry keys (Uninstall key and Google Update update key). + HKEY reg_root = system_uninstall ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + RegKey key(reg_root, L"", KEY_ALL_ACCESS); + DeleteRegistryKey(key, installer_util::kUninstallRegPath); + DeleteRegistryKey(key, InstallUtil::GetChromeGoogleUpdateKey()); + + // Delete Software\Classes\ChromeHTML, + // Software\Clients\StartMenuInternet\chrome.exe and + // Software\RegisteredApplications\Chrome + std::wstring html_prog_id(ShellUtil::kRegClasses); + file_util::AppendToPath(&html_prog_id, ShellUtil::kChromeHTMLProgId); + DeleteRegistryKey(key, html_prog_id); + + std::wstring set_access_key(ShellUtil::kRegStartMenuInternet); + file_util::AppendToPath(&set_access_key, installer_util::kChromeExe); + DeleteRegistryKey(key, set_access_key); + + DeleteRegistryValue(reg_root, ShellUtil::kRegRegisteredApplications, + installer_util::kApplicationName); + key.Close(); + + // Delete shared registry keys as well (these require admin rights) if + // remove_all option is specified. + if (remove_all) { + RegKey hklm_key(HKEY_LOCAL_MACHINE, L"", KEY_ALL_ACCESS); + DeleteRegistryKey(hklm_key, set_access_key); + DeleteRegistryKey(hklm_key, html_prog_id); + DeleteRegistryValue(HKEY_LOCAL_MACHINE, + ShellUtil::kRegRegisteredApplications, + installer_util::kApplicationName); + + // Delete media player registry key that exists only in HKLM. + std::wstring reg_path(installer::kMediaPlayerRegPath); + file_util::AppendToPath(®_path, installer_util::kChromeExe); + DeleteRegistryKey(hklm_key, reg_path); + hklm_key.Close(); + } + + // Finally delete all the files from Chrome folder after moving setup.exe + // to a temp location. + std::wstring install_path(installer::GetChromeInstallPath(system_uninstall)); + if (install_path.empty()) { + LOG(ERROR) << "Couldn't get installation destination path"; + // Nothing else we could do for uninstall, so we return. + return installer_util::UNINSTALL_FAILED; + } else { + LOG(INFO) << "install destination path: " << install_path; + } + + std::wstring setup_exe(install_path); + file_util::AppendToPath(&setup_exe, installed_version.GetString()); + file_util::AppendToPath(&setup_exe, installer::kInstallerDir); + file_util::AppendToPath(&setup_exe, file_util::GetFilenameFromPath(exe_path)); + + std::wstring temp_file; + file_util::CreateTemporaryFileName(&temp_file); + file_util::Move(setup_exe, temp_file); + + LOG(INFO) << "Deleting install path " << install_path; + if (!file_util::Delete(install_path, true)) + LOG(ERROR) << "Failed to delete folder: " << install_path; + + LOG(INFO) << "Uninstallation complete. Launching Uninstall survey."; + LaunchUninstallSurvey(installed_version); + return installer_util::UNINSTALL_SUCCESSFUL; +} diff --git a/chrome/installer/setup/uninstall.h b/chrome/installer/setup/uninstall.h new file mode 100644 index 0000000..9308859 --- /dev/null +++ b/chrome/installer/setup/uninstall.h @@ -0,0 +1,56 @@ +// 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 Chrome uninstall related functions. + +#ifndef CHROME_INSTALLER_SETUP_UNINSTALL_H__ +#define CHROME_INSTALLER_SETUP_UNINSTALL_H__ + +#include <string> + +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/version.h" + +namespace installer_setup { +// This function uninstalls Chrome. +// +// exe_path: Path to the executable (setup.exe) as it will be copied +// to temp folder before deleting Chrome folder. +// system_uninstall: if true, the function uninstalls Chrome installed system +// wise. otherwise, it uninstalls Chrome installed for the +// current user. +// installed_version: currently installed version of Chrome. +// remove_all: Remove all shared files, registry entries as well. +installer_util::InstallStatus UninstallChrome( + const std::wstring& exe_path, bool system_uninstall, + const installer::Version& installed_version, bool remove_all); + +} // namespace installer_setup + +#endif // CHROME_INSTALLER_SETUP_UNINSTALL_H__ 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)); +} |